@@ -4,7 +4,7 @@ package immutable
4
4
5
5
import strawman .collection .mutable .{ArrayBuffer , Builder }
6
6
7
- import scala .{Any , AnyRef , Boolean , Int , None , NoSuchElementException , noinline , Nothing , Option , PartialFunction , Some , StringContext , Unit , UnsupportedOperationException , deprecated }
7
+ import scala .{Any , AnyRef , Boolean , Int , NoSuchElementException , None , Nothing , Option , PartialFunction , Some , StringContext , Unit , UnsupportedOperationException , deprecated , noinline }
8
8
import scala .Predef .String
9
9
import scala .annotation .tailrec
10
10
import scala .annotation .unchecked .uncheckedVariance
@@ -230,37 +230,30 @@ sealed private[immutable] trait LazyListOps[+A, +CC[+X] <: LinearSeq[X] with Laz
230
230
231
231
protected [this ] def cons [T ](hd : => T , tl : => CC [T ]): CC [T ]
232
232
233
- /** Force the evaluation of both the head and the tail of this `LazyList` */
234
- def force : Option [(A , CC [A ])]
233
+ /** Forces evaluation of the whole `LazyList` and returns it.
234
+ *
235
+ * @note Often we use `LazyList`s to represent an infinite set or series. If
236
+ * that's the case for your particular `LazyList` then this function will never
237
+ * return and will probably crash the VM with an `OutOfMemory` exception.
238
+ * This function will not hang on a finite cycle, however.
239
+ *
240
+ * @return The fully realized `LazyList`.
241
+ */
242
+ def force : this .type
235
243
236
244
/** The stream resulting from the concatenation of this stream with the argument stream.
237
245
*
238
246
* @param suffix The collection that gets appended to this lazy list
239
247
* @return The lazy list containing elements of this lazy list and the iterable object.
240
248
*/
241
249
def lazyAppendAll [B >: A ](suffix : => collection.IterableOnce [B ]): CC [B ] =
242
- if (isEmpty) iterableFactory.from(suffix.iterator() ) else cons[B ](head, tail.lazyAppendAll(suffix))
250
+ if (isEmpty) iterableFactory.from(suffix) else cons[B ](head, tail.lazyAppendAll(suffix))
243
251
244
252
override def className = " LazyList"
245
253
246
254
override def equals (that : Any ): Boolean =
247
255
if (this eq that.asInstanceOf [AnyRef ]) true else super .equals(that)
248
256
249
- override def sameElements [B >: A ](that : IterableOnce [B ]): Boolean = {
250
- @ tailrec def lazyListEq (a : CC [_], b : CC [_]): Boolean =
251
- if (a eq b) true else {
252
- (a.force, b.force) match {
253
- case (Some ((ah, at)), Some ((bh, bt))) => (ah == bh) && lazyListEq(at, bt)
254
- case (None , None ) => true
255
- case _ => false
256
- }
257
- }
258
- that match {
259
- case that : LazyListOps [_, _, _] => lazyListEq(coll, that.asInstanceOf [CC [_]])
260
- case _ => super .sameElements(that)
261
- }
262
- }
263
-
264
257
override def scanLeft [B ](z : B )(op : (B , A ) => B ): CC [B ] =
265
258
if (isEmpty) z +: iterableFactory.empty
266
259
else cons(z, tail.scanLeft(op(z, head))(op))
@@ -337,11 +330,11 @@ sealed private[immutable] trait LazyListOps[+A, +CC[+X] <: LinearSeq[X] with Laz
337
330
else {
338
331
// establish !prefix.isEmpty || nonEmptyPrefix.isEmpty
339
332
var nonEmptyPrefix : CC [A ] = coll
340
- var prefix = iterableFactory.from(f(nonEmptyPrefix.head).iterator() )
333
+ var prefix = iterableFactory.from(f(nonEmptyPrefix.head))
341
334
while (! nonEmptyPrefix.isEmpty && prefix.isEmpty) {
342
335
nonEmptyPrefix = nonEmptyPrefix.tail
343
336
if (! nonEmptyPrefix.isEmpty)
344
- prefix = iterableFactory.from(f(nonEmptyPrefix.head).iterator() )
337
+ prefix = iterableFactory.from(f(nonEmptyPrefix.head))
345
338
}
346
339
347
340
if (nonEmptyPrefix.isEmpty) iterableFactory.empty
@@ -434,7 +427,6 @@ sealed private[immutable] trait LazyListFactory[+CC[+X] <: LinearSeq[X] with Laz
434
427
newCons(head, stream.tail.collect(pf))
435
428
}
436
429
437
- type Evaluated [+ A ] <: Option [(A , CC [A ])]
438
430
}
439
431
440
432
/**
@@ -446,14 +438,12 @@ object LazyList extends LazyListFactory[LazyList] {
446
438
447
439
protected [this ] def newCons [T ](hd : => T , tl : => LazyList [T ]): LazyList [T ] = new LazyList .Cons (hd, tl)
448
440
449
- type Evaluated [+ A ] = Option [(A , LazyList [A ])]
450
-
451
441
object Empty extends LazyList [Nothing ] {
452
442
override def isEmpty : Boolean = true
453
443
override def head : Nothing = throw new NoSuchElementException (" head of empty lazy list" )
454
444
override def tail : LazyList [Nothing ] = throw new UnsupportedOperationException (" tail of empty lazy list" )
455
- def force : Evaluated [ Nothing ] = None
456
- override def toString : String = " Empty"
445
+ def force : this . type = this
446
+ override def toString () : String = " Empty"
457
447
}
458
448
459
449
final class Cons [A ](hd : => A , tl : => LazyList [A ]) extends LazyList [A ] {
@@ -468,8 +458,26 @@ object LazyList extends LazyListFactory[LazyList] {
468
458
tlEvaluated = true
469
459
tl
470
460
}
471
- def force : Evaluated [A ] = Some ((head, tail))
472
- override def toString : String =
461
+ def force : this .type = {
462
+ // Use standard 2x 1x iterator trick for cycle detection ("those" is slow one)
463
+ var these, those : LazyList [A ] = this
464
+ if (! these.isEmpty) {
465
+ these.head
466
+ these = these.tail
467
+ }
468
+ while (those ne these) {
469
+ if (these.isEmpty) return this
470
+ these.head
471
+ these = these.tail
472
+ if (these.isEmpty) return this
473
+ these.head
474
+ these = these.tail
475
+ if (these eq those) return this
476
+ those = those.tail
477
+ }
478
+ this
479
+ }
480
+ override def toString (): String =
473
481
if (hdEvaluated) s " $head #:: ${if (tlEvaluated) tail.toString else " ?" }"
474
482
else " LazyList(?)"
475
483
}
@@ -499,7 +507,8 @@ object LazyList extends LazyListFactory[LazyList] {
499
507
}
500
508
501
509
object #:: {
502
- def unapply [A ](s : LazyList [A ]): Evaluated [A ] = s.force
510
+ def unapply [A ](s : LazyList [A ]): Option [(A , LazyList [A ])] =
511
+ if (s.nonEmpty) Some ((s.head, s.tail)) else None
503
512
}
504
513
505
514
def from [A ](coll : collection.IterableOnce [A ]): LazyList [A ] = coll match {
@@ -571,14 +580,21 @@ object Stream extends LazyListFactory[Stream] {
571
580
572
581
protected [this ] def newCons [T ](hd : => T , tl : => Stream [T ]): Stream [T ] = new Stream .Cons (hd, tl)
573
582
574
- type Evaluated [+ A ] = Option [(A , Stream [A ])]
575
-
576
583
object Empty extends Stream [Nothing ] {
577
584
override def isEmpty : Boolean = true
578
585
override def head : Nothing = throw new NoSuchElementException (" head of empty lazy list" )
579
586
override def tail : Stream [Nothing ] = throw new UnsupportedOperationException (" tail of empty lazy list" )
580
- def force : Evaluated [Nothing ] = None
581
- override def toString : String = " Empty"
587
+ /** Forces evaluation of the whole `Stream` and returns it.
588
+ *
589
+ * @note Often we use `Stream`s to represent an infinite set or series. If
590
+ * that's the case for your particular `Stream` then this function will never
591
+ * return and will probably crash the VM with an `OutOfMemory` exception.
592
+ * This function will not hang on a finite cycle, however.
593
+ *
594
+ * @return The fully realized `Stream`.
595
+ */
596
+ def force : this .type = this
597
+ override def toString (): String = " Empty"
582
598
}
583
599
584
600
final class Cons [A ](override val head : A , tl : => Stream [A ]) extends Stream [A ] {
@@ -588,9 +604,32 @@ object Stream extends LazyListFactory[Stream] {
588
604
tlEvaluated = true
589
605
tl
590
606
}
591
- def force : Evaluated [A ] = Some ((head, tail))
592
- override def toString : String =
607
+ /** Forces evaluation of the whole `Stream` and returns it.
608
+ *
609
+ * @note Often we use `Stream`s to represent an infinite set or series. If
610
+ * that's the case for your particular `Stream` then this function will never
611
+ * return and will probably crash the VM with an `OutOfMemory` exception.
612
+ * This function will not hang on a finite cycle, however.
613
+ *
614
+ * @return The fully realized `Stream`.
615
+ */
616
+ def force : this .type = {
617
+ // Use standard 2x 1x iterator trick for cycle detection ("those" is slow one)
618
+ var these, those : Stream [A ] = this
619
+ if (! these.isEmpty) these = these.tail
620
+ while (those ne these) {
621
+ if (these.isEmpty) return this
622
+ these = these.tail
623
+ if (these.isEmpty) return this
624
+ these = these.tail
625
+ if (these eq those) return this
626
+ those = those.tail
627
+ }
628
+ this
629
+ }
630
+ override def toString (): String =
593
631
s " $head #:: ${if (tlEvaluated) tail.toString else " ?" }"
632
+
594
633
}
595
634
596
635
/** An alternative way of building and matching Streams using Stream.cons(hd, tl).
@@ -618,7 +657,8 @@ object Stream extends LazyListFactory[Stream] {
618
657
}
619
658
620
659
object #:: {
621
- def unapply [A ](s : Stream [A ]): Evaluated [A ] = s.force
660
+ def unapply [A ](s : Stream [A ]): Option [(A , Stream [A ])] =
661
+ if (s.nonEmpty) Some ((s.head, s.tail)) else None
622
662
}
623
663
624
664
def from [A ](coll : collection.IterableOnce [A ]): Stream [A ] = coll match {
0 commit comments