Skip to content

Commit 01f2958

Browse files
committed
Add lazyZip to ArrayOps and StringOps
- Also simplify the definition of lazyZip for collections. There is no need for extension methods and LazyZipOps. lazyZip can be defined as a regular method. - This alone makes it available for Array and String types but we want to get back an Array or String (if applicable) so we still need separate definitions in ArrayOps and StringOps. Fixes scala/bug#11063
1 parent 19f2280 commit 01f2958

File tree

5 files changed

+79
-46
lines changed

5 files changed

+79
-46
lines changed

library/src/scala/collection/ArrayOps.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,25 @@ final class ArrayOps[A](val xs: Array[A]) extends AnyVal {
903903
b.result()
904904
}
905905

906+
/** Analogous to `zip` except that the elements in each collection are not consumed until a strict operation is
907+
* invoked on the returned `LazyZip2` decorator.
908+
*
909+
* Calls to `lazyZip` can be chained to support higher arities (up to 4) without incurring the expense of
910+
* constructing and deconstructing intermediary tuples.
911+
*
912+
* {{{
913+
* val xs = List(1, 2, 3)
914+
* val res = (xs lazyZip xs lazyZip xs lazyZip xs).map((a, b, c, d) => a + b + c + d)
915+
* // res == List(4, 8, 12)
916+
* }}}
917+
*
918+
* @param that the iterable providing the second element of each eventual pair
919+
* @tparam B the type of the second element in each eventual pair
920+
* @return a decorator `LazyZip2` that allows strict operations to be performed on the lazily evaluated pairs
921+
* or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported.
922+
*/
923+
def lazyZip[B](that: Iterable[B]): LazyZip2[A, B, Array[A]] = new LazyZip2(xs, immutable.ArraySeq.unsafeWrapArray(xs), that)
924+
906925
/** Returns an array formed from this array and another iterable collection
907926
* by combining corresponding elements in pairs.
908927
* If one of the two collections is shorter than the other,

library/src/scala/collection/Iterable.scala

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,25 @@ trait Iterable[+A] extends IterableOnce[A] with IterableOps[A, Iterable, Iterabl
8181
* by all elements separated by commas and enclosed in parentheses.
8282
*/
8383
override def toString = mkString(className + "(", ", ", ")")
84+
85+
/** Analogous to `zip` except that the elements in each collection are not consumed until a strict operation is
86+
* invoked on the returned `LazyZip2` decorator.
87+
*
88+
* Calls to `lazyZip` can be chained to support higher arities (up to 4) without incurring the expense of
89+
* constructing and deconstructing intermediary tuples.
90+
*
91+
* {{{
92+
* val xs = List(1, 2, 3)
93+
* val res = (xs lazyZip xs lazyZip xs lazyZip xs).map((a, b, c, d) => a + b + c + d)
94+
* // res == List(4, 8, 12)
95+
* }}}
96+
*
97+
* @param that the iterable providing the second element of each eventual pair
98+
* @tparam B the type of the second element in each eventual pair
99+
* @return a decorator `LazyZip2` that allows strict operations to be performed on the lazily evaluated pairs
100+
* or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported.
101+
*/
102+
def lazyZip[B](that: Iterable[B]): LazyZip2[A, B, this.type] = new LazyZip2(this, this, that)
84103
}
85104

86105
/** Base trait for Iterable operations
@@ -846,9 +865,7 @@ object IterableOps {
846865
}
847866

848867
@SerialVersionUID(3L)
849-
object Iterable extends IterableFactory.Delegate[Iterable](immutable.Iterable) {
850-
implicit def toLazyZipOps[A, CC[X] <: Iterable[X]](that: CC[A]): LazyZipOps[A, CC[A]] = new LazyZipOps(that)
851-
}
868+
object Iterable extends IterableFactory.Delegate[Iterable](immutable.Iterable)
852869

853870
/** Explicit instantiation of the `Iterable` trait to reduce class file size in subclasses. */
854871
@SerialVersionUID(3L)

library/src/scala/collection/LazyZipOps.scala

Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,8 @@ package scala.collection
22

33
import scala.language.implicitConversions
44

5-
final class LazyZipOps[A, C1 <: Iterable[A]] private[collection](val `this`: C1) extends AnyVal {
6-
7-
/** Analogous to `zip` except that the elements in each collection are not consumed until a strict operation is
8-
* invoked on the returned `LazyZip2` decorator.
9-
*
10-
* Calls to `lazyZip` can be chained to support higher arities (up to 4) without incurring the expense of
11-
* constructing and deconstructing intermediary tuples.
12-
*
13-
* {{{
14-
* val xs = List(1, 2, 3)
15-
* val res = (xs lazyZip xs lazyZip xs lazyZip xs).map((a, b, c, d) => a + b + c + d)
16-
* // res == List(4, 8, 12)
17-
* }}}
18-
*
19-
* @param that the iterable providing the second element of each eventual pair
20-
* @tparam B the type of the second element in each eventual pair
21-
* @return a decorator `LazyZip2` that allows strict operations to be performed on the lazily evaluated pairs
22-
* or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported.
23-
*/
24-
def lazyZip[B](that: Iterable[B]): LazyZip2[A, B, C1] = new LazyZip2(`this`, that)
25-
}
26-
275
/** Decorator representing lazily zipped pairs. */
28-
final class LazyZip2[El1, El2, C1 <: Iterable[El1]] private[collection](coll1: C1, coll2: Iterable[El2]) {
6+
final class LazyZip2[+El1, +El2, C1] private[collection](src: C1, coll1: Iterable[El1], coll2: Iterable[El2]) {
297

308
/** Zips `that` iterable collection with an existing `LazyZip2`. The elements in each collection are
319
* not consumed until a strict operation is invoked on the returned `LazyZip3` decorator.
@@ -35,10 +13,10 @@ final class LazyZip2[El1, El2, C1 <: Iterable[El1]] private[collection](coll1: C
3513
* @return a decorator `LazyZip3` that allows strict operations to be performed on the lazily evaluated tuples or
3614
* chained calls to `lazyZip`. Implicit conversion to `Iterable[(El1, El2, B)]` is also supported.
3715
*/
38-
def lazyZip[B](that: Iterable[B]): LazyZip3[El1, El2, B, C1] = new LazyZip3(coll1, coll2, that)
16+
def lazyZip[B](that: Iterable[B]): LazyZip3[El1, El2, B, C1] = new LazyZip3(src, coll1, coll2, that)
3917

4018
def map[B, C](f: (El1, El2) => B)(implicit bf: BuildFrom[C1, B, C]): C = {
41-
bf.fromSpecificIterable(coll1)(new AbstractView[B] {
19+
bf.fromSpecificIterable(src)(new AbstractView[B] {
4220
def iterator = new AbstractIterator[B] {
4321
private[this] val elems1 = coll1.iterator
4422
private[this] val elems2 = coll2.iterator
@@ -51,7 +29,7 @@ final class LazyZip2[El1, El2, C1 <: Iterable[El1]] private[collection](coll1: C
5129
}
5230

5331
def flatMap[B, C](f: (El1, El2) => Iterable[B])(implicit bf: BuildFrom[C1, B, C]): C = {
54-
bf.fromSpecificIterable(coll1)(new AbstractView[B] {
32+
bf.fromSpecificIterable(src)(new AbstractView[B] {
5533
def iterator = new AbstractIterator[B] {
5634
private[this] val elems1 = coll1.iterator
5735
private[this] val elems2 = coll2.iterator
@@ -70,7 +48,7 @@ final class LazyZip2[El1, El2, C1 <: Iterable[El1]] private[collection](coll1: C
7048
}
7149

7250
def filter[C](p: (El1, El2) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2), C]): C = {
73-
bf.fromSpecificIterable(coll1)(new AbstractView[(El1, El2)] {
51+
bf.fromSpecificIterable(src)(new AbstractView[(El1, El2)] {
7452
def iterator = new AbstractIterator[(El1, El2)] {
7553
private[this] val elems1 = coll1.iterator
7654
private[this] val elems2 = coll2.iterator
@@ -136,9 +114,10 @@ object LazyZip2 {
136114

137115

138116
/** Decorator representing lazily zipped triples. */
139-
final class LazyZip3[El1, El2, El3, C1 <: Iterable[El1]] private[collection](coll1: C1,
140-
coll2: Iterable[El2],
141-
coll3: Iterable[El3]) {
117+
final class LazyZip3[+El1, +El2, +El3, C1] private[collection](src: C1,
118+
coll1: Iterable[El1],
119+
coll2: Iterable[El2],
120+
coll3: Iterable[El3]) {
142121

143122
/** Zips `that` iterable collection with an existing `LazyZip3`. The elements in each collection are
144123
* not consumed until a strict operation is invoked on the returned `LazyZip4` decorator.
@@ -148,10 +127,10 @@ final class LazyZip3[El1, El2, El3, C1 <: Iterable[El1]] private[collection](col
148127
* @return a decorator `LazyZip4` that allows strict operations to be performed on the lazily evaluated tuples.
149128
* Implicit conversion to `Iterable[(El1, El2, El3, B)]` is also supported.
150129
*/
151-
def lazyZip[B](that: Iterable[B]): LazyZip4[El1, El2, El3, B, C1] = new LazyZip4(coll1, coll2, coll3, that)
130+
def lazyZip[B](that: Iterable[B]): LazyZip4[El1, El2, El3, B, C1] = new LazyZip4(src, coll1, coll2, coll3, that)
152131

153132
def map[B, C](f: (El1, El2, El3) => B)(implicit bf: BuildFrom[C1, B, C]): C = {
154-
bf.fromSpecificIterable(coll1)(new AbstractView[B] {
133+
bf.fromSpecificIterable(src)(new AbstractView[B] {
155134
def iterator = new AbstractIterator[B] {
156135
private[this] val elems1 = coll1.iterator
157136
private[this] val elems2 = coll2.iterator
@@ -165,7 +144,7 @@ final class LazyZip3[El1, El2, El3, C1 <: Iterable[El1]] private[collection](col
165144
}
166145

167146
def flatMap[B, C](f: (El1, El2, El3) => Iterable[B])(implicit bf: BuildFrom[C1, B, C]): C = {
168-
bf.fromSpecificIterable(coll1)(new AbstractView[B] {
147+
bf.fromSpecificIterable(src)(new AbstractView[B] {
169148
def iterator = new AbstractIterator[B] {
170149
private[this] val elems1 = coll1.iterator
171150
private[this] val elems2 = coll2.iterator
@@ -185,7 +164,7 @@ final class LazyZip3[El1, El2, El3, C1 <: Iterable[El1]] private[collection](col
185164
}
186165

187166
def filter[C](p: (El1, El2, El3) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2, El3), C]): C = {
188-
bf.fromSpecificIterable(coll1)(new AbstractView[(El1, El2, El3)] {
167+
bf.fromSpecificIterable(src)(new AbstractView[(El1, El2, El3)] {
189168
def iterator = new AbstractIterator[(El1, El2, El3)] {
190169
private[this] val elems1 = coll1.iterator
191170
private[this] val elems2 = coll2.iterator
@@ -259,13 +238,14 @@ object LazyZip3 {
259238

260239

261240
/** Decorator representing lazily zipped 4-tuples. */
262-
final class LazyZip4[El1, El2, El3, El4, C1 <: Iterable[El1]] private[collection](coll1: C1,
263-
coll2: Iterable[El2],
264-
coll3: Iterable[El3],
265-
coll4: Iterable[El4]) {
241+
final class LazyZip4[+El1, +El2, +El3, +El4, C1] private[collection](src: C1,
242+
coll1: Iterable[El1],
243+
coll2: Iterable[El2],
244+
coll3: Iterable[El3],
245+
coll4: Iterable[El4]) {
266246

267247
def map[B, C](f: (El1, El2, El3, El4) => B)(implicit bf: BuildFrom[C1, B, C]): C = {
268-
bf.fromSpecificIterable(coll1)(new AbstractView[B] {
248+
bf.fromSpecificIterable(src)(new AbstractView[B] {
269249
def iterator = new AbstractIterator[B] {
270250
private[this] val elems1 = coll1.iterator
271251
private[this] val elems2 = coll2.iterator
@@ -280,7 +260,7 @@ final class LazyZip4[El1, El2, El3, El4, C1 <: Iterable[El1]] private[collection
280260
}
281261

282262
def flatMap[B, C](f: (El1, El2, El3, El4) => Iterable[B])(implicit bf: BuildFrom[C1, B, C]): C = {
283-
bf.fromSpecificIterable(coll1)(new AbstractView[B] {
263+
bf.fromSpecificIterable(src)(new AbstractView[B] {
284264
def iterator = new AbstractIterator[B] {
285265
private[this] val elems1 = coll1.iterator
286266
private[this] val elems2 = coll2.iterator
@@ -301,7 +281,7 @@ final class LazyZip4[El1, El2, El3, El4, C1 <: Iterable[El1]] private[collection
301281
}
302282

303283
def filter[C](p: (El1, El2, El3, El4) => Boolean)(implicit bf: BuildFrom[C1, (El1, El2, El3, El4), C]): C = {
304-
bf.fromSpecificIterable(coll1)(new AbstractView[(El1, El2, El3, El4)] {
284+
bf.fromSpecificIterable(src)(new AbstractView[(El1, El2, El3, El4)] {
305285
def iterator = new AbstractIterator[(El1, El2, El3, El4)] {
306286
private[this] val elems1 = coll1.iterator
307287
private[this] val elems2 = coll2.iterator

library/src/scala/collection/Map.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,6 @@ object MapOps {
330330
*/
331331
@SerialVersionUID(3L)
332332
object Map extends MapFactory.Delegate[Map](immutable.Map) {
333-
implicit def toLazyZipOps[K, V, CC[X, Y] <: Iterable[(X, Y)]](that: CC[K, V]): LazyZipOps[(K, V), CC[K, V]] = new LazyZipOps(that)
334-
335333
private val DefaultSentinel: AnyRef = new AnyRef
336334
}
337335

library/src/scala/collection/StringOps.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,25 @@ final class StringOps(private val s: String) extends AnyVal {
12891289
(res1.toString, res2.toString)
12901290
}
12911291

1292+
/** Analogous to `zip` except that the elements in each collection are not consumed until a strict operation is
1293+
* invoked on the returned `LazyZip2` decorator.
1294+
*
1295+
* Calls to `lazyZip` can be chained to support higher arities (up to 4) without incurring the expense of
1296+
* constructing and deconstructing intermediary tuples.
1297+
*
1298+
* {{{
1299+
* val xs = List(1, 2, 3)
1300+
* val res = (xs lazyZip xs lazyZip xs lazyZip xs).map((a, b, c, d) => a + b + c + d)
1301+
* // res == List(4, 8, 12)
1302+
* }}}
1303+
*
1304+
* @param that the iterable providing the second element of each eventual pair
1305+
* @tparam B the type of the second element in each eventual pair
1306+
* @return a decorator `LazyZip2` that allows strict operations to be performed on the lazily evaluated pairs
1307+
* or chained calls to `lazyZip`. Implicit conversion to `Iterable[(A, B)]` is also supported.
1308+
*/
1309+
def lazyZip[B](that: Iterable[B]): LazyZip2[Char, B, String] = new LazyZip2(s, new WrappedString(s), that)
1310+
12921311

12931312
/* ************************************************************************************************************
12941313
The remaining methods are provided for completeness but they delegate to WrappedString implementations which

0 commit comments

Comments
 (0)