Skip to content

Commit 217cf05

Browse files
bors[bot]cuviper
andcommitted
604: impl ParallelExtend for tuple pairs r=nikomatsakis a=cuviper `ParallelExtend<(A, B)>` and `ParallelExtend<Either<L, R>>` for tuples behave like `unzip` and `partition_map` respectively. These allow the possibility of nested `unzip` and `partition_map` operations, filling into more than just two collections. For instance, `(A, (B, C))` items can be unzipped into `(Vec<A>, (Vec<B>, Vec<C>))`. Fixes rayon-rs#600. Co-authored-by: Josh Stone <[email protected]>
2 parents fbc2bb5 + e46efd2 commit 217cf05

File tree

2 files changed

+118
-20
lines changed

2 files changed

+118
-20
lines changed

src/iter/mod.rs

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,20 @@ pub trait ParallelIterator: Sized + Send {
15191519
/// assert_eq!(left, [0, 1, 2, 3]);
15201520
/// assert_eq!(right, [1, 2, 3, 4]);
15211521
/// ```
1522+
///
1523+
/// Nested pairs can be unzipped too.
1524+
///
1525+
/// ```
1526+
/// use rayon::prelude::*;
1527+
///
1528+
/// let (values, (squares, cubes)): (Vec<_>, (Vec<_>, Vec<_>)) = (0..4).into_par_iter()
1529+
/// .map(|i| (i, (i * i, i * i * i)))
1530+
/// .unzip();
1531+
///
1532+
/// assert_eq!(values, [0, 1, 2, 3]);
1533+
/// assert_eq!(squares, [0, 1, 4, 9]);
1534+
/// assert_eq!(cubes, [0, 1, 8, 27]);
1535+
/// ```
15221536
fn unzip<A, B, FromA, FromB>(self) -> (FromA, FromB)
15231537
where Self: ParallelIterator<Item = (A, B)>,
15241538
FromA: Default + Send + ParallelExtend<A>,
@@ -1567,17 +1581,38 @@ pub trait ParallelIterator: Sized + Send {
15671581
/// use rayon::iter::Either;
15681582
///
15691583
/// let (left, right): (Vec<_>, Vec<_>) = (0..8).into_par_iter()
1570-
/// .partition_map(|x| {
1571-
/// if x % 2 == 0 {
1572-
/// Either::Left(x * 4)
1573-
/// } else {
1574-
/// Either::Right(x * 3)
1575-
/// }
1576-
/// });
1584+
/// .partition_map(|x| {
1585+
/// if x % 2 == 0 {
1586+
/// Either::Left(x * 4)
1587+
/// } else {
1588+
/// Either::Right(x * 3)
1589+
/// }
1590+
/// });
15771591
///
15781592
/// assert_eq!(left, [0, 8, 16, 24]);
15791593
/// assert_eq!(right, [3, 9, 15, 21]);
15801594
/// ```
1595+
///
1596+
/// Nested `Either` enums can be split as well.
1597+
///
1598+
/// ```
1599+
/// use rayon::prelude::*;
1600+
/// use rayon::iter::Either::*;
1601+
///
1602+
/// let ((fizzbuzz, fizz), (buzz, other)): ((Vec<_>, Vec<_>), (Vec<_>, Vec<_>)) = (1..20)
1603+
/// .into_par_iter()
1604+
/// .partition_map(|x| match (x % 3, x % 5) {
1605+
/// (0, 0) => Left(Left(x)),
1606+
/// (0, _) => Left(Right(x)),
1607+
/// (_, 0) => Right(Left(x)),
1608+
/// (_, _) => Right(Right(x)),
1609+
/// });
1610+
///
1611+
/// assert_eq!(fizzbuzz, [15]);
1612+
/// assert_eq!(fizz, [3, 6, 9, 12, 18]);
1613+
/// assert_eq!(buzz, [5, 10]);
1614+
/// assert_eq!(other, [1, 2, 4, 7, 8, 11, 13, 14, 16, 17, 19]);
1615+
/// ```
15811616
fn partition_map<A, B, P, L, R>(self, predicate: P) -> (A, B)
15821617
where A: Default + Send + ParallelExtend<L>,
15831618
B: Default + Send + ParallelExtend<R>,

src/iter/unzip.rs

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ trait UnzipOp<T>: Sync + Send {
2424
}
2525
}
2626

27-
/// Run an unzip-like operation into `ParallelExtend` collections.
27+
/// Run an unzip-like operation into default `ParallelExtend` collections.
2828
fn execute<I, OP, FromA, FromB>(pi: I, op: OP) -> (FromA, FromB)
2929
where I: ParallelIterator,
3030
OP: UnzipOp<I::Item>,
@@ -33,21 +33,30 @@ fn execute<I, OP, FromA, FromB>(pi: I, op: OP) -> (FromA, FromB)
3333
{
3434
let mut a = FromA::default();
3535
let mut b = FromB::default();
36-
{
37-
// We have no idea what the consumers will look like for these
38-
// collections' `par_extend`, but we can intercept them in our own
39-
// `drive_unindexed`. Start with the left side, type `A`:
40-
let iter = UnzipA {
41-
base: pi,
42-
op: op,
43-
b: &mut b,
44-
};
45-
a.par_extend(iter);
46-
}
36+
execute_into(&mut a, &mut b, pi, op);
4737
(a, b)
4838
}
4939

5040

41+
/// Run an unzip-like operation into `ParallelExtend` collections.
42+
fn execute_into<I, OP, FromA, FromB>(a: &mut FromA, b: &mut FromB, pi: I, op: OP)
43+
where I: ParallelIterator,
44+
OP: UnzipOp<I::Item>,
45+
FromA: Send + ParallelExtend<OP::Left>,
46+
FromB: Send + ParallelExtend<OP::Right>
47+
{
48+
// We have no idea what the consumers will look like for these
49+
// collections' `par_extend`, but we can intercept them in our own
50+
// `drive_unindexed`. Start with the left side, type `A`:
51+
let iter = UnzipA {
52+
base: pi,
53+
op: op,
54+
b: b,
55+
};
56+
a.par_extend(iter);
57+
}
58+
59+
5160
/// Unzips the items of a parallel iterator into a pair of arbitrary
5261
/// `ParallelExtend` containers.
5362
///
@@ -188,7 +197,7 @@ struct UnzipA<'b, I, OP, FromB: 'b> {
188197
impl<'b, I, OP, FromB> ParallelIterator for UnzipA<'b, I, OP, FromB>
189198
where I: ParallelIterator,
190199
OP: UnzipOp<I::Item>,
191-
FromB: Default + Send + ParallelExtend<OP::Right>
200+
FromB: Send + ParallelExtend<OP::Right>
192201
{
193202
type Item = OP::Left;
194203

@@ -386,3 +395,57 @@ impl<A, B, RA, RB> Reducer<(A, B)> for UnzipReducer<RA, RB>
386395
(self.left.reduce(left.0, right.0), self.right.reduce(left.1, right.1))
387396
}
388397
}
398+
399+
400+
impl<A, B, FromA, FromB> ParallelExtend<(A, B)> for (FromA, FromB)
401+
where
402+
A: Send,
403+
B: Send,
404+
FromA: Send + ParallelExtend<A>,
405+
FromB: Send + ParallelExtend<B>,
406+
{
407+
fn par_extend<I>(&mut self, pi: I)
408+
where
409+
I: IntoParallelIterator<Item = (A, B)>,
410+
{
411+
execute_into(&mut self.0, &mut self.1, pi.into_par_iter(), Unzip);
412+
}
413+
}
414+
415+
impl<L, R, A, B> ParallelExtend<Either<L, R>> for (A, B)
416+
where
417+
L: Send,
418+
R: Send,
419+
A: Send + ParallelExtend<L>,
420+
B: Send + ParallelExtend<R>,
421+
{
422+
fn par_extend<I>(&mut self, pi: I)
423+
where
424+
I: IntoParallelIterator<Item = Either<L, R>>,
425+
{
426+
execute_into(&mut self.0, &mut self.1, pi.into_par_iter(), UnEither);
427+
}
428+
}
429+
430+
/// An `UnzipOp` that routes items depending on their `Either` variant.
431+
struct UnEither;
432+
433+
impl<L, R> UnzipOp<Either<L, R>> for UnEither
434+
where
435+
L: Send,
436+
R: Send,
437+
{
438+
type Left = L;
439+
type Right = R;
440+
441+
fn consume<FL, FR>(&self, item: Either<L, R>, left: FL, right: FR) -> (FL, FR)
442+
where
443+
FL: Folder<L>,
444+
FR: Folder<R>,
445+
{
446+
match item {
447+
Either::Left(item) => (left.consume(item), right),
448+
Either::Right(item) => (left, right.consume(item)),
449+
}
450+
}
451+
}

0 commit comments

Comments
 (0)