diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/HashSetBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/HashSetBenchmark.scala index cb90a0e2b4..0c02eab96e 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/HashSetBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/HashSetBenchmark.scala @@ -138,6 +138,12 @@ class HashSetBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ImmutableArrayBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ImmutableArrayBenchmark.scala index 28177841a3..01ecdee72b 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ImmutableArrayBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ImmutableArrayBenchmark.scala @@ -276,6 +276,12 @@ class ImmutableArrayBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/LazyListBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/LazyListBenchmark.scala index 70fc3d2f7d..f453b9c40a 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/LazyListBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/LazyListBenchmark.scala @@ -276,6 +276,12 @@ class LazyListBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ListBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ListBenchmark.scala index 95d458b0c2..61565440fe 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ListBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ListBenchmark.scala @@ -276,6 +276,12 @@ class ListBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/NumericRangeBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/NumericRangeBenchmark.scala index 65caa7a82c..28020432d9 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/NumericRangeBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/NumericRangeBenchmark.scala @@ -4,7 +4,6 @@ import java.util.concurrent.TimeUnit import org.openjdk.jmh.annotations._ import org.openjdk.jmh.infra.Blackhole -import strawman.collection.Tuple2Zipped import scala.{Any, AnyRef, Int, Long, Unit, math} import scala.Predef.intWrapper @@ -183,6 +182,12 @@ class NumericRangeBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Int, b: Int) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/RangeBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/RangeBenchmark.scala index f9ec9e7897..d9ed0fbc91 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/RangeBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/RangeBenchmark.scala @@ -4,7 +4,6 @@ import java.util.concurrent.TimeUnit import org.openjdk.jmh.annotations._ import org.openjdk.jmh.infra.Blackhole -import strawman.collection.Tuple2Zipped import scala.{Any, AnyRef, Int, Long, Unit, math} import scala.Predef.intWrapper @@ -183,6 +182,12 @@ class RangeBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Int, b: Int) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaHashSetBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaHashSetBenchmark.scala index f78b063bce..0a193f7f46 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaHashSetBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaHashSetBenchmark.scala @@ -138,6 +138,12 @@ class ScalaHashSetBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaListBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaListBenchmark.scala index f83422ac07..1c44f850f0 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaListBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaListBenchmark.scala @@ -273,6 +273,12 @@ class ScalaListBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaTreeSetBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaTreeSetBenchmark.scala index c68f76d390..061d6550c8 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaTreeSetBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaTreeSetBenchmark.scala @@ -138,6 +138,12 @@ class ScalaTreeSetBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaVectorBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaVectorBenchmark.scala index 4910c8bcec..9afe931450 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaVectorBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/ScalaVectorBenchmark.scala @@ -273,6 +273,12 @@ class ScalaVectorBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/TreeSetBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/TreeSetBenchmark.scala index 380e01121e..2dd4dd4dec 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/TreeSetBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/TreeSetBenchmark.scala @@ -138,6 +138,12 @@ class TreeSetBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/immutable/VectorBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/immutable/VectorBenchmark.scala index 749e084da3..ff8fb68b3c 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/immutable/VectorBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/immutable/VectorBenchmark.scala @@ -276,6 +276,12 @@ class VectorBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/mutable/ArrayBufferBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/mutable/ArrayBufferBenchmark.scala index 5ccc998399..f8a3531f31 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/mutable/ArrayBufferBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/mutable/ArrayBufferBenchmark.scala @@ -276,6 +276,12 @@ class ArrayBufferBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/mutable/ListBufferBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/mutable/ListBufferBenchmark.scala index 35981ec12b..225286ff8a 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/mutable/ListBufferBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/mutable/ListBufferBenchmark.scala @@ -276,6 +276,12 @@ class ListBufferBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/benchmarks/time/src/main/scala/strawman/collection/mutable/ScalaArrayBenchmark.scala b/benchmarks/time/src/main/scala/strawman/collection/mutable/ScalaArrayBenchmark.scala index 2bec5ca9fd..11c4bfd44e 100644 --- a/benchmarks/time/src/main/scala/strawman/collection/mutable/ScalaArrayBenchmark.scala +++ b/benchmarks/time/src/main/scala/strawman/collection/mutable/ScalaArrayBenchmark.scala @@ -275,6 +275,12 @@ class ScalaArrayBenchmark { @Benchmark def transform_zip(bh: Blackhole): Unit = bh.consume(xs.zip(xs)) + @Benchmark + def transform_zipMapTupled(bh: Blackhole): Unit = { + val f = (a: Long, b: Long) => (a, b) + bh.consume(xs.zip(xs).map(f.tupled)) + } + @Benchmark def transform_zipWithIndex(bh: Blackhole): Unit = bh.consume(xs.zipWithIndex) diff --git a/collections/src/main/scala/strawman/collection/Iterable.scala b/collections/src/main/scala/strawman/collection/Iterable.scala index 66cd2e2cea..a854fe1ade 100644 --- a/collections/src/main/scala/strawman/collection/Iterable.scala +++ b/collections/src/main/scala/strawman/collection/Iterable.scala @@ -2,6 +2,7 @@ package strawman package collection import scala.annotation.unchecked.uncheckedVariance +import scala.language.implicitConversions import scala.reflect.ClassTag import scala.{Any, Array, Boolean, `inline`, Int, None, Numeric, Option, Ordering, PartialFunction, StringContext, Some, Unit, deprecated, IllegalArgumentException, Function1, AnyRef} import java.lang.{String, UnsupportedOperationException} @@ -1004,29 +1005,7 @@ trait IterableOps[+A, +CC[_], +C] extends Any with IterableOnce[A] { } object Iterable extends IterableFactory.Delegate[Iterable](immutable.Iterable) { - - implicit class LazyZipOps[A, C1[X] <: Iterable[X]](`this`: C1[A]) { - - /** Analogous to `zip` except that the elements in each collection are not consumed until a strict operation is - * invoked on the returned `Tuple2Zipped` decorator. - * - * Calls to `lazyZip` can be chained to support higher arities (up to 4) without incurring the expense of - * constructing and deconstructing intermediary tuples. - * - * {{{ - * val xs = $Coll(1, 2, 3) - * val res = (xs lazyZip xs lazyZip xs lazyZip xs).map((a, b, c, d) => a + b + c + d) - * // res == $Col(4, 8, 12) - * }}} - * - * @param that the iterable providing the second half of each eventual pair - * @tparam B the type of the element in the second half of each eventual pair - * @tparam C2 the type of `that` iterable - * @return a decorator `Tuple2Zipped` that allows strict operations to be performed on the lazily evaluated pairs - * or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported. - */ - def lazyZip[B, C2[X] <: Iterable[X]](that: C2[B]): Tuple2Zipped[A, C1[A], B, C2[B]] = new Tuple2Zipped(`this`, that) - } + implicit def toLazyZipOps[A, CC[X] <: Iterable[X]](that: CC[A]): LazyZipOps[A, CC[A]] = new LazyZipOps(that) } abstract class AbstractIterable[+A] extends Iterable[A] \ No newline at end of file diff --git a/collections/src/main/scala/strawman/collection/TupleZipped.scala b/collections/src/main/scala/strawman/collection/LazyZipOps.scala similarity index 79% rename from collections/src/main/scala/strawman/collection/TupleZipped.scala rename to collections/src/main/scala/strawman/collection/LazyZipOps.scala index e4a05d0962..4e2c7da5de 100644 --- a/collections/src/main/scala/strawman/collection/TupleZipped.scala +++ b/collections/src/main/scala/strawman/collection/LazyZipOps.scala @@ -3,19 +3,40 @@ package strawman.collection import scala.{Boolean, StringContext, Unit} import scala.language.implicitConversions +final class LazyZipOps[A, C1 <: Iterable[A]] private[collection](`this`: C1) { + + /** Analogous to `zip` except that the elements in each collection are not consumed until a strict operation is + * invoked on the returned `Tuple2Zipped` decorator. + * + * Calls to `lazyZip` can be chained to support higher arities (up to 4) without incurring the expense of + * constructing and deconstructing intermediary tuples. + * + * {{{ + * val xs = List(1, 2, 3) + * val res = (xs lazyZip xs lazyZip xs lazyZip xs).map((a, b, c, d) => a + b + c + d) + * // res == List(4, 8, 12) + * }}} + * + * @param that the iterable providing the second half of each eventual pair + * @tparam B the type of the element in the second half of each eventual pair + * @return a decorator `Tuple2Zipped` that allows strict operations to be performed on the lazily evaluated pairs + * or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported. + */ + def lazyZip[B](that: Iterable[B]): Tuple2Zipped[A, B, C1] = new Tuple2Zipped(`this`, that) +} + /** Decorator representing lazily zipped tuples of arity 2. */ -final class Tuple2Zipped[El1, C1 <: Iterable[El1], El2, C2 <: Iterable[El2]] private[collection](coll1: C1, coll2: C2) { +final class Tuple2Zipped[El1, El2, C1 <: Iterable[El1]] private[collection](coll1: C1, coll2: Iterable[El2]) { /** Zips `that` iterable collection with an already lazily zipped `Tuple2Zipped`. The elements in each collection are * not consumed until a strict operation is invoked on the returned `Tuple3Zipped` decorator. * * @param that the iterable providing the third element of each eventual tuple * @tparam B the type of the third element in each eventual tuple - * @tparam C3 the type of `that` iterable * @return a decorator `Tuple3Zipped` that allows strict operations to be performed on the lazily evaluated tuples or * chained calls to `lazyZip`. Implicit conversion to `Iterable[(El1, El2, B)]` is also supported. */ - def lazyZip[B, C3[X] <: Iterable[X]](that: C3[B]): Tuple3Zipped[El1, C1, El2, C2, B, C3[B]] = new Tuple3Zipped(coll1, coll2, that) + def lazyZip[B](that: Iterable[B]): Tuple3Zipped[El1, El2, B, C1] = new Tuple3Zipped(coll1, coll2, that) def map[B, C](f: (El1, El2) => B)(implicit bf: BuildFrom[C1, B, C]): C = { bf.fromSpecificIterable(coll1)(new View[B] { @@ -39,7 +60,6 @@ final class Tuple2Zipped[El1, C1 <: Iterable[El1], El2, C2 <: Iterable[El2]] pri _current = f(elems1.next(), elems2.next()).iterator() _current } - def hasNext = current.hasNext def next() = current.next() } @@ -98,33 +118,29 @@ final class Tuple2Zipped[El1, C1 <: Iterable[El1], El2, C2 <: Iterable[El2]] pri def next() = (elems1.next(), elems2.next()) } - def className = getClass.getName - - override def toString = s"$className($coll1, $coll2)" + override def toString = s"$coll1.lazyZip($coll2)" } object Tuple2Zipped { - implicit def tuple2ZippedToIterable[El1, C1 <: Iterable[El1], - El2, C2 <: Iterable[El2]](zipped2: Tuple2Zipped[El1, C1, El2, C2]): Iterable[(El1, El2)] = + implicit def tuple2ZippedToIterable[El1, El2](zipped2: Tuple2Zipped[El1, El2, _]): View[(El1, El2)] = new View[(El1, El2)] { def iterator() = zipped2.iterator() } } /** Decorator representing lazily zipped tuples of arity 3. */ -final class Tuple3Zipped[El1, C1 <: Iterable[El1], - El2, C2 <: Iterable[El2], - El3, C3 <: Iterable[El3]] private[collection](coll1: C1, coll2: C2, coll3: C3) { +final class Tuple3Zipped[El1, El2, El3, C1 <: Iterable[El1]] private[collection](coll1: C1, + coll2: Iterable[El2], + coll3: Iterable[El3]) { /** Zips `that` iterable collection with an already lazily zipped `Tuple3Zipped`. The elements in each collection are * not consumed until a strict operation is invoked on the returned `Tuple4Zipped` decorator. * - * @param that the iterable providing the forth element of each eventual tuple - * @tparam B the type of the forth element in each eventual tuple - * @tparam C4 the type of `that` iterable + * @param that the iterable providing the fourth element of each eventual tuple + * @tparam B the type of the fourth element in each eventual tuple * @return a decorator `Tuple4Zipped` that allows strict operations to be performed on the lazily evaluated tuples. * Implicit conversion to `Iterable[(El1, El2, El3, B)]` is also supported. */ - def lazyZip[B, C4[X] <: Iterable[X]](that: C4[B]): Tuple4Zipped[El1, C1, El2, C2, El3, C3, B, C4[B]] = new Tuple4Zipped(coll1, coll2, coll3, that) + def lazyZip[B](that: Iterable[B]): Tuple4Zipped[El1, El2, El3, B, C1] = new Tuple4Zipped(coll1, coll2, coll3, that) def map[B, C](f: (El1, El2, El3) => B)(implicit bf: BuildFrom[C1, B, C]): C = { bf.fromSpecificIterable(coll1)(new View[B] { @@ -150,7 +166,6 @@ final class Tuple3Zipped[El1, C1 <: Iterable[El1], _current = f(elems1.next(), elems2.next(), elems3.next()).iterator() _current } - def hasNext = current.hasNext def next() = current.next() } @@ -216,25 +231,21 @@ final class Tuple3Zipped[El1, C1 <: Iterable[El1], def next() = (elems1.next(), elems2.next(), elems3.next()) } - def className = getClass.getName - - override def toString = s"$className($coll1, $coll2, $coll3)" + override def toString = s"$coll1.lazyZip($coll2).lazyZip($coll3)" } object Tuple3Zipped { - implicit def tuple3ZippedToIterable[El1, C1 <: Iterable[El1], - El2, C2 <: Iterable[El2], - El3, C3 <: Iterable[El3]](zipped3: Tuple3Zipped[El1, C1, El2, C2, El3, C3]): Iterable[(El1, El2, El3)] = + implicit def tuple3ZippedToIterable[El1, El2, El3](zipped3: Tuple3Zipped[El1, El2, El3, _]): View[(El1, El2, El3)] = new View[(El1, El2, El3)] { def iterator() = zipped3.iterator() } } /** Decorator representing lazily zipped tuples of arity 4. */ -final class Tuple4Zipped[El1, C1 <: Iterable[El1], - El2, C2 <: Iterable[El2], - El3, C3 <: Iterable[El3], - El4, C4 <: Iterable[El4]] private[collection](coll1: C1, coll2: C2, coll3: C3, coll4: C4) { +final class Tuple4Zipped[El1, El2, El3, El4, C1 <: Iterable[El1]] private[collection](coll1: C1, + coll2: Iterable[El2], + coll3: Iterable[El3], + coll4: Iterable[El4]) { def map[B, C](f: (El1, El2, El3, El4) => B)(implicit bf: BuildFrom[C1, B, C]): C = { bf.fromSpecificIterable(coll1)(new View[B] { @@ -262,7 +273,6 @@ final class Tuple4Zipped[El1, C1 <: Iterable[El1], _current = f(elems1.next(), elems2.next(), elems3.next(), elems4.next()).iterator() _current } - def hasNext = current.hasNext def next() = current.next() } @@ -333,15 +343,11 @@ final class Tuple4Zipped[El1, C1 <: Iterable[El1], def next() = (elems1.next(), elems2.next(), elems3.next(), elems4.next()) } - def className = getClass.getName - - override def toString = s"$className($coll1, $coll2, $coll3, $coll4)" + override def toString = s"$coll1.lazyZip($coll2).lazyZip($coll3).lazyZip($coll4)" } object Tuple4Zipped { - implicit def tuple4ZippedToIterable[El1, C1 <: Iterable[El1], - El2, C2 <: Iterable[El2], - El3, C3 <: Iterable[El3], - El4, C4 <: Iterable[El4]](zipped4: Tuple4Zipped[El1, C1, El2, C2, El3, C3, El4, C4]): Iterable[(El1, El2, El3, El4)] = + implicit def tuple4ZippedToIterable[El1, El2, El3, El4](zipped4: Tuple4Zipped[El1, El2, El3, El4, _] + ): View[(El1, El2, El3, El4)] = new View[(El1, El2, El3, El4)] { def iterator() = zipped4.iterator() } } \ No newline at end of file diff --git a/collections/src/main/scala/strawman/collection/Map.scala b/collections/src/main/scala/strawman/collection/Map.scala index 3dd1eb2318..0d69fde7fb 100644 --- a/collections/src/main/scala/strawman/collection/Map.scala +++ b/collections/src/main/scala/strawman/collection/Map.scala @@ -6,6 +6,7 @@ import collection.mutable.Builder import scala.{Any, Boolean, ClassCastException, Equals, Int, NoSuchElementException, None, Nothing, Option, Ordering, PartialFunction, Serializable, Some, StringContext, `inline`, throws} import scala.Predef.String import scala.annotation.unchecked.uncheckedVariance +import scala.language.implicitConversions import scala.util.hashing.MurmurHash3 /** Base Map type */ @@ -242,7 +243,9 @@ trait MapOps[K, +V, +CC[X, Y] <: MapOps[X, Y, CC, _], +C <: MapOps[K, V, CC, C]] * @define coll map * @define Coll `Map` */ -object Map extends MapFactory.Delegate[Map](immutable.Map) +object Map extends MapFactory.Delegate[Map](immutable.Map) { + implicit def toLazyZipOps[K, V, CC[X, Y] <: Iterable[(X, Y)]](that: CC[K, V]): LazyZipOps[(K, V), CC[K, V]] = new LazyZipOps(that) +} /** Explicit instantiation of the `Map` trait to reduce class file size in subclasses. */ abstract class AbstractMap[A, +B] extends AbstractIterable[(A, B)] with Map[A, B] diff --git a/test/junit/src/test/scala/strawman/collection/TupleZippedTest.scala b/test/junit/src/test/scala/strawman/collection/LazyZipOpsTest.scala similarity index 61% rename from test/junit/src/test/scala/strawman/collection/TupleZippedTest.scala rename to test/junit/src/test/scala/strawman/collection/LazyZipOpsTest.scala index 46ceef0601..fed3f25def 100644 --- a/test/junit/src/test/scala/strawman/collection/TupleZippedTest.scala +++ b/test/junit/src/test/scala/strawman/collection/LazyZipOpsTest.scala @@ -1,13 +1,14 @@ package strawman.collection +import org.hamcrest.CoreMatchers._ import org.junit.Assert._ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import strawman.collection.immutable.List +import strawman.collection.immutable._ @RunWith(classOf[JUnit4]) -class TupleZippedTest { +class LazyZipOpsTest { private val ws = List(1, 2, 3) private val xs = List(1, 2, 3, 4, 5, 6) @@ -16,6 +17,9 @@ class TupleZippedTest { private val zipped2 = ws lazyZip xs private val zipped3 = ws lazyZip xs lazyZip ys private val zipped4 = ws lazyZip xs lazyZip ys lazyZip zs + private val map = Map(1 -> "foo" , 2 -> "bar") + private val sortedMap = TreeMap(1 -> "foo" , 2 -> "bar") + private val sortedSet = TreeSet(1, 2, 3) @Test def tuple2Zipped_map(): Unit = { @@ -67,8 +71,32 @@ class TupleZippedTest { @Test def tuple2Zipped_empty(): Unit = { - assertTrue(List.empty.lazyZip(xs).isEmpty) - assertTrue(xs.lazyZip(List.empty).isEmpty) + assertTrue(Nil.lazyZip(xs).isEmpty) + assertTrue(xs.lazyZip(Nil).isEmpty) + } + + @Test + def tuple2Zipped_withOrdering(): Unit = { + val res: TreeSet[Int] = sortedSet.lazyZip(ws).map(_ + _) + + assertEquals(TreeSet(2, 4, 6), res) + } + + @Test + def tuple2Zipped_withMap(): Unit = { + val res: Map[Int, (String, String)] = map.lazyZip(ys).map { case ((k, v), s) => k -> (s, v) } + + assertThat(res, either( + is(Map(1 -> ("a", "foo"), 2 -> ("b", "bar")))) + .or(is(Map(1 -> ("b", "foo"), 2 -> ("a", "bar")))) + ) + } + + @Test + def tuple2Zipped_withSortedMap(): Unit = { + val res: TreeMap[Int, (String, String)] = sortedMap.lazyZip(ys).map { case ((k, v), s) => k -> (s, v) } + + assertEquals(Map(1 -> ("a", "foo"), 2 -> ("b", "bar")), res) } @Test @@ -121,7 +149,33 @@ class TupleZippedTest { @Test def tuple3Zipped_empty(): Unit = { - assertTrue(zipped2.lazyZip(List.empty).isEmpty) + assertTrue(zipped2.lazyZip(Nil).isEmpty) + assertTrue(Nil.lazyZip(Nil).lazyZip(xs).isEmpty) + } + + @Test + def tuple3Zipped_withOrdering(): Unit = { + val res: TreeSet[Int] = sortedSet.lazyZip(xs).lazyZip(ws).map(_ + _ + _) + + assertEquals(TreeSet(3, 6, 9), res) + } + + @Test + def tuple3Zipped_withMap(): Unit = { + val res: Map[Int, (Int, String, String)] = map.lazyZip(ws).lazyZip(ys).map { case ((k, v), w, y) => k -> (w, y, v) } + + assertThat(res, either( + is(Map(1 -> (1, "a", "foo"), 2 -> (2, "b", "bar")))) + .or(is(Map(1 -> (2, "b", "foo"), 2 -> (1, "a", "bar")))) + ) + } + + @Test + def tuple3Zipped_withSortedMap(): Unit = { + val res: TreeMap[Int, (Int, String, String)] = sortedMap.lazyZip(ws).lazyZip(ys) + .map { case ((k, v), w, y) => k -> (w, y, v) } + + assertEquals(Map(1 -> (1, "a", "foo"), 2 -> (2, "b", "bar")), res) } @Test @@ -174,6 +228,33 @@ class TupleZippedTest { @Test def tuple4Zipped_empty(): Unit = { - assertTrue(zipped3.lazyZip(List.empty).isEmpty) + assertTrue(zipped3.lazyZip(Nil).isEmpty) + assertTrue(Nil.lazyZip(Nil).lazyZip(Nil).lazyZip(xs).isEmpty) + } + + @Test + def tuple4Zipped_withOrdering(): Unit = { + val res: TreeSet[Int] = sortedSet.lazyZip(xs).lazyZip(ws).lazyZip(ws).map(_ + _ + _ + _) + + assertEquals(TreeSet(4, 8, 12), res) + } + + @Test + def tuple4Zipped_withMap(): Unit = { + val res: Map[Int, (Int, Int, String, String)] = map.lazyZip(ws).lazyZip(xs).lazyZip(ys) + .map { case ((k, v), w, x, y) => k -> (w, x, y, v) } + + assertThat(res, either( + is(Map(1 -> (1, 1, "a", "foo"), 2 -> (2, 2, "b", "bar")))) + .or(is(Map(1 -> (2, 2, "b", "foo"), 2 -> (1, 1, "a", "bar")))) + ) + } + + @Test + def tuple4Zipped_withSortedMap(): Unit = { + val res: TreeMap[Int, (Int, Int, String, String)] = sortedMap.lazyZip(ws).lazyZip(xs).lazyZip(ys) + .map { case ((k, v), w, x, y) => k -> (w, x, y, v) } + + assertEquals(Map(1 -> (1, 1, "a", "foo"), 2 -> (2, 2, "b", "bar")), res) } } \ No newline at end of file