From 680f57cdd9b2d683009d355c5c5d286c9998c7ad Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 19 Nov 2020 11:26:10 -0800 Subject: [PATCH] upgrade to Scala 2.13.4 --- .travis.yml | 2 +- build.sbt | 2 +- .../compat/immutable/LazyList.scala | 1550 ++++++++--------- .../collection/LazyListLazinessTest.scala | 177 +- 4 files changed, 779 insertions(+), 952 deletions(-) diff --git a/.travis.yml b/.travis.yml index e954bfae..df8f2d0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ language: scala scala: - 2.11.12 - 2.12.12 - - 2.13.3 + - 2.13.4 - 3.0.0-M1 env: diff --git a/build.sbt b/build.sbt index 04adfa5f..c3b739c2 100644 --- a/build.sbt +++ b/build.sbt @@ -52,7 +52,7 @@ lazy val junit = libraryDependencies += "com.novocode" % "junit-interface" % "0. lazy val scala211 = "2.11.12" lazy val scala212 = "2.12.12" -lazy val scala213 = "2.13.3" +lazy val scala213 = "2.13.4" lazy val scala30 = "3.0.0-M1" lazy val compat = MultiScalaCrossProject(JSPlatform, JVMPlatform, NativePlatform)( diff --git a/compat/src/main/scala-2.11_2.12/scala/collection/compat/immutable/LazyList.scala b/compat/src/main/scala-2.11_2.12/scala/collection/compat/immutable/LazyList.scala index 3e4d73ff..6a1cb80b 100644 --- a/compat/src/main/scala-2.11_2.12/scala/collection/compat/immutable/LazyList.scala +++ b/compat/src/main/scala-2.11_2.12/scala/collection/compat/immutable/LazyList.scala @@ -10,239 +10,253 @@ * additional information regarding copyright ownership. */ -package scala.collection.compat.immutable +package scala +package collection +package immutable import java.io.{ObjectInputStream, ObjectOutputStream} +import java.lang.{StringBuilder => JStringBuilder} import scala.annotation.tailrec -import scala.annotation.unchecked.{uncheckedVariance => uV} -import scala.collection.{ - AbstractIterator, - AbstractSeq, - GenIterable, - GenSeq, - GenTraversableOnce, - LinearSeqOptimized, - mutable -} -import scala.collection.generic.{ - CanBuildFrom, - FilterMonadic, - GenericCompanion, - GenericTraversableTemplate, - SeqFactory -} -import scala.collection.immutable.{LinearSeq, NumericRange} -import scala.collection.mutable.{ArrayBuffer, Builder, StringBuilder} +import scala.collection.generic.SerializeEnd +import scala.collection.mutable.{ArrayBuffer, Builder, ReusableBuilder, StringBuilder} import scala.language.implicitConversions - -/** This class implements an immutable linked list that evaluates elements - * in order and only when needed. Here is an example: - * - * {{{ - * import scala.math.BigInt - * object Main extends App { - * - * val fibs: LazyList[BigInt] = BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map { n => n._1 + n._2 } - * - * fibs take 5 foreach println - * } - * - * // prints - * // - * // 0 - * // 1 - * // 1 - * // 2 - * // 3 - * }}} - * - * A `LazyList`, like the one in the example above, may be infinite in length. - * Aggregate methods, such as `count`, `sum`, `max` or `min` on such infinite length - * sequences will not terminate. Filtered infinite lazy lists are also effectively - * infinite in length. - * - * Elements of a `LazyList` are memoized; that is, the value of each element - * is computed only once. - * To illustrate, we will alter body of the `fibs` value above and take some - * more values: - * - * {{{ - * import scala.math.BigInt - * object Main extends App { - * - * val fibs: LazyList[BigInt] = BigInt(0) #:: BigInt(1) #:: fibs.zip( - * fibs.tail).map(n => { - * println("Adding %d and %d".format(n._1, n._2)) - * n._1 + n._2 - * }) - * - * fibs take 5 foreach println - * fibs take 6 foreach println - * } - * - * // prints - * // - * // 0 - * // 1 - * // Adding 0 and 1 - * // 1 - * // Adding 1 and 1 - * // 2 - * // Adding 1 and 2 - * // 3 - * - * // And then prints - * // - * // 0 - * // 1 - * // 1 - * // 2 - * // 3 - * // Adding 2 and 3 - * // 5 - * }}} - * - * There are a number of subtle points to the above example. - * - * - The definition of `fibs` is a `val` not a method. The memoization of the - * `LazyList` requires us to have somewhere to store the information and a `val` - * allows us to do that. - * - * - While the `LazyList` is actually being modified during access, this does not - * change the notion of its immutability. Once the values are memoized they do - * not change and values that have yet to be memoized still "exist", they - * simply haven't been realized yet. - * - * - One must be cautious of memoization; you can very quickly eat up large - * amounts of memory if you're not careful. The reason for this is that the - * memoization of the `LazyList` creates a structure much like - * [[scala.collection.immutable.List]]. So long as something is holding on to - * the head, the head holds on to the tail, and so it continues recursively. - * If, on the other hand, there is nothing holding on to the head (e.g. we used - * `def` to define the `LazyList`) then once it is no longer being used directly, - * it disappears. - * - * - Note that some operations, including [[drop]], [[dropWhile]], - * [[flatMap]] or [[collect]] may process a large number of intermediate - * elements before returning. These necessarily hold onto the head, since - * they are methods on `LazyList`, and a lazy list holds its own head. For - * computations of this sort where memoization is not desired, use - * `Iterator` when possible. - * - * {{{ - * // For example, let's build the natural numbers and do some silly iteration - * // over them. - * - * // We'll start with a silly iteration - * def loop(s: String, i: Int, iter: Iterator[Int]): Unit = { - * // Stop after 200,000 - * if (i < 200001) { - * if (i % 50000 == 0) println(s + i) - * loop(s, iter.next(), iter) - * } - * } - * - * // Our first LazyList definition will be a val definition - * val lazylist1: LazyList[Int] = { - * def loop(v: Int): LazyList[Int] = v #:: loop(v + 1) - * loop(0) - * } - * - * // Because lazylist1 is a val, everything that the iterator produces is held - * // by virtue of the fact that the head of the LazyList is held in lazylist1 - * val it1 = lazylist1.toIterator - * loop("Iterator1: ", it1.next(), it1) - * - * // We can redefine this LazyList such that all we have is the Iterator left - * // and allow the LazyList to be garbage collected as required. Using a def - * // to provide the LazyList ensures that no val is holding onto the head as - * // is the case with lazylist1 - * def lazylist2: LazyList[Int] = { - * def loop(v: Int): LazyList[Int] = v #:: loop(v + 1) - * loop(0) - * } - * val it2 = lazylist2.toIterator - * loop("Iterator2: ", it2.next(), it2) - * - * // And, of course, we don't actually need a LazyList at all for such a simple - * // problem. There's no reason to use a LazyList if you don't actually need - * // one. - * val it3 = new Iterator[Int] { - * var i = -1 - * def hasNext = true - * def next(): Int = { i += 1; i } - * } - * loop("Iterator3: ", it3.next(), it3) - * }}} - * - * - The fact that `tail` works at all is of interest. In the definition of - * `fibs` we have an initial `(0, 1, LazyList(...))` so `tail` is deterministic. - * If we defined `fibs` such that only `0` were concretely known then the act - * of determining `tail` would require the evaluation of `tail` which would - * cause an infinite recursion and stack overflow. If we define a definition - * where the tail is not initially computable then we're going to have an - * infinite recursion: - * {{{ - * // The first time we try to access the tail we're going to need more - * // information which will require us to recurse, which will require us to - * // recurse, which... - * lazy val sov: LazyList[Vector[Int]] = Vector(0) #:: sov.zip(sov.tail).map { n => n._1 ++ n._2 } - * }}} - * - * The definition of `fibs` above creates a larger number of objects than - * necessary depending on how you might want to implement it. The following - * implementation provides a more "cost effective" implementation due to the - * fact that it has a more direct route to the numbers themselves: - * - * {{{ - * lazy val fib: LazyList[Int] = { - * def loop(h: Int, n: Int): LazyList[Int] = h #:: loop(n, h + n) - * loop(1, 1) - * } - * }}} - * - * @tparam A the type of the elements contained in this lazy list. - * - * @see [[http://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#lazylists "Scala's Collection Library overview"]] - * section on `LazyLists` for more information. - * @define Coll `LazyList` - * @define coll lazy list - * @define orderDependent - * @define orderDependentFold - * @define appendStackSafety Note: Repeated chaining of calls to append methods (`appended`, - * `appendedAll`, `lazyAppendedAll`) without forcing any of the - * intermediate resulting lazy lists may overflow the stack when - * the final result is forced. - * @define preservesLaziness This method preserves laziness; elements are only evaluated - * individually as needed. - * @define initiallyLazy This method does not evaluate anything until an operation is performed - * on the result (e.g. calling `head` or `tail`, or checking if it is empty). - * @define evaluatesAllElements This method evaluates all elements of the collection. - */ +import scala.runtime.Statics + +/** This class implements an immutable linked list. We call it "lazy" + * because it computes its elements only when they are needed. + * + * Elements are memoized; that is, the value of each element is computed at most once. + * + * Elements are computed in-order and are never skipped. In other words, + * accessing the tail causes the head to be computed first. + * + * How lazy is a `LazyList`? When you have a value of type `LazyList`, you + * don't know yet whether the list is empty or not. If you learn that it is non-empty, + * then you also know that the head has been computed. But the tail is itself + * a `LazyList`, whose emptiness-or-not might remain undetermined. + * + * A `LazyList` may be infinite. For example, `LazyList.from(0)` contains + * all of the natural numbers 0, 1, 2, and so on. For infinite sequences, + * some methods (such as `count`, `sum`, `max` or `min`) will not terminate. + * + * Here is an example: + * + * {{{ + * import scala.math.BigInt + * object Main extends App { + * val fibs: LazyList[BigInt] = + * BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map{ n => n._1 + n._2 } + * fibs.take(5).foreach(println) + * } + * + * // prints + * // + * // 0 + * // 1 + * // 1 + * // 2 + * // 3 + * }}} + * + * To illustrate, let's add some output to the definition `fibs`, so we + * see what's going on. + * + * {{{ + * import scala.math.BigInt + * object Main extends App { + * val fibs: LazyList[BigInt] = + * BigInt(0) #:: BigInt(1) #:: + * fibs.zip(fibs.tail).map{ n => + * println(s"Adding ${n._1} and ${n._2}") + * n._1 + n._2 + * } + * fibs.take(5).foreach(println) + * fibs.take(6).foreach(println) + * } + * + * // prints + * // + * // 0 + * // 1 + * // Adding 0 and 1 + * // 1 + * // Adding 1 and 1 + * // 2 + * // Adding 1 and 2 + * // 3 + * + * // And then prints + * // + * // 0 + * // 1 + * // 1 + * // 2 + * // 3 + * // Adding 2 and 3 + * // 5 + * }}} + * + * Note that the definition of `fibs` uses `val` not `def`. The memoization of the + * `LazyList` requires us to have somewhere to store the information and a `val` + * allows us to do that. + * + * Further remarks about the semantics of `LazyList`: + * + * - Though the `LazyList` changes as it is accessed, this does not + * contradict its immutability. Once the values are memoized they do + * not change. Values that have yet to be memoized still "exist", they + * simply haven't been computed yet. + * + * - One must be cautious of memoization; it can eat up memory if you're not + * careful. That's because memoization of the `LazyList` creates a structure much like + * [[scala.collection.immutable.List]]. As long as something is holding on to + * the head, the head holds on to the tail, and so on recursively. + * If, on the other hand, there is nothing holding on to the head (e.g. if we used + * `def` to define the `LazyList`) then once it is no longer being used directly, + * it disappears. + * + * - Note that some operations, including [[drop]], [[dropWhile]], + * [[flatMap]] or [[collect]] may process a large number of intermediate + * elements before returning. + * + * Here's another example. Let's start with the natural numbers and iterate + * over them. + * + * {{{ + * // We'll start with a silly iteration + * def loop(s: String, i: Int, iter: Iterator[Int]): Unit = { + * // Stop after 200,000 + * if (i < 200001) { + * if (i % 50000 == 0) println(s + i) + * loop(s, iter.next(), iter) + * } + * } + * + * // Our first LazyList definition will be a val definition + * val lazylist1: LazyList[Int] = { + * def loop(v: Int): LazyList[Int] = v #:: loop(v + 1) + * loop(0) + * } + * + * // Because lazylist1 is a val, everything that the iterator produces is held + * // by virtue of the fact that the head of the LazyList is held in lazylist1 + * val it1 = lazylist1.iterator + * loop("Iterator1: ", it1.next(), it1) + * + * // We can redefine this LazyList such that all we have is the Iterator left + * // and allow the LazyList to be garbage collected as required. Using a def + * // to provide the LazyList ensures that no val is holding onto the head as + * // is the case with lazylist1 + * def lazylist2: LazyList[Int] = { + * def loop(v: Int): LazyList[Int] = v #:: loop(v + 1) + * loop(0) + * } + * val it2 = lazylist2.iterator + * loop("Iterator2: ", it2.next(), it2) + * + * // And, of course, we don't actually need a LazyList at all for such a simple + * // problem. There's no reason to use a LazyList if you don't actually need + * // one. + * val it3 = new Iterator[Int] { + * var i = -1 + * def hasNext = true + * def next(): Int = { i += 1; i } + * } + * loop("Iterator3: ", it3.next(), it3) + * }}} + * + * - In the `fibs` example earlier, the fact that `tail` works at all is of interest. + * `fibs` has an initial `(0, 1, LazyList(...))`, so `tail` is deterministic. + * If we defined `fibs` such that only `0` were concretely known, then the act + * of determining `tail` would require the evaluation of `tail`, so the + * computation would be unable to progress, as in this code: + * {{{ + * // The first time we try to access the tail we're going to need more + * // information which will require us to recurse, which will require us to + * // recurse, which... + * lazy val sov: LazyList[Vector[Int]] = Vector(0) #:: sov.zip(sov.tail).map { n => n._1 ++ n._2 } + * }}} + * + * The definition of `fibs` above creates a larger number of objects than + * necessary depending on how you might want to implement it. The following + * implementation provides a more "cost effective" implementation due to the + * fact that it has a more direct route to the numbers themselves: + * + * {{{ + * lazy val fib: LazyList[Int] = { + * def loop(h: Int, n: Int): LazyList[Int] = h #:: loop(n, h + n) + * loop(1, 1) + * } + * }}} + * + * The head, the tail and whether the list is empty or not can be initially unknown. + * Once any of those are evaluated, they are all known, though if the tail is + * built with `#::` or `#:::`, it's content still isn't evaluated. Instead, evaluating + * the tails content is deferred until the tails empty status, head or tail is + * evaluated. + * + * Delaying the evaluation of whether a LazyList is empty or not until it's needed + * allows LazyList to not eagerly evaluate any elements on a call to `filter`. + * + * Only when it's further evaluated (which may be never!) any of the elements gets + * forced. + * + * for example: + * + * {{{ + * def tailWithSideEffect: LazyList[Nothing] = { + * println("getting empty LazyList") + * LazyList.empty + * } + * + * val emptyTail = tailWithSideEffect // prints "getting empty LazyList" + * + * val suspended = 1 #:: tailWithSideEffect // doesn't print anything + * val tail = suspended.tail // although the tail is evaluated, *still* nothing is yet printed + * val filtered = tail.filter(_ => false) // still nothing is printed + * filtered.isEmpty // prints "getting empty LazyList" + * }}} + * + * @tparam A the type of the elements contained in this lazy list. + * + * @see [[http://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#lazylists "Scala's Collection Library overview"]] + * section on `LazyLists` for more information. + * @define Coll `LazyList` + * @define coll lazy list + * @define orderDependent + * @define orderDependentFold + * @define appendStackSafety Note: Repeated chaining of calls to append methods (`appended`, + * `appendedAll`, `lazyAppendedAll`) without forcing any of the + * intermediate resulting lazy lists may overflow the stack when + * the final result is forced. + * @define preservesLaziness This method preserves laziness; elements are only evaluated + * individually as needed. + * @define initiallyLazy This method does not evaluate anything until an operation is performed + * on the result (e.g. calling `head` or `tail`, or checking if it is empty). + * @define evaluatesAllElements This method evaluates all elements of the collection. + */ @SerialVersionUID(3L) -final class LazyList[+A] private (private[this] var lazyState: () => LazyList.State[A]) - extends AbstractSeq[A] +final class LazyList[+A] private(private[this] var lazyState: () => LazyList.State[A]) + extends AbstractSeq[A] with LinearSeq[A] - with GenericTraversableTemplate[A, LazyList] - with LinearSeqOptimized[A, LazyList[A]] + with LinearSeqOps[A, LazyList, LazyList[A]] + with IterableFactoryDefaults[A, LazyList] with Serializable { import LazyList._ @volatile private[this] var stateEvaluated: Boolean = false - @inline private def stateDefined: Boolean = stateEvaluated - private[this] var midEvaluation = false + @inline private def stateDefined: Boolean = stateEvaluated + private[this] var midEvaluation = false private lazy val state: State[A] = { // if it's already mid-evaluation, we're stuck in an infinite // self-referential loop (also it's empty) if (midEvaluation) { - throw new RuntimeException( - "self-referential LazyList or a derivation thereof has no more elements") + throw new RuntimeException("self-referential LazyList or a derivation thereof has no more elements") } midEvaluation = true - val res = try lazyState() - finally midEvaluation = false + val res = try lazyState() finally midEvaluation = false // if we set it to `true` before evaluating, we may infinite loop // if something expects `state` to already be evaluated stateEvaluated = true @@ -250,46 +264,42 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St res } - /** $preservesLaziness */ - def knownSize: Int = if (knownIsEmpty) 0 else -1 -// override def iterableFactory: SeqFactory[LazyList] = LazyList + override def iterableFactory: SeqFactory[LazyList] = LazyList override def isEmpty: Boolean = state eq State.Empty + /** @inheritdoc + * + * $preservesLaziness + */ + override def knownSize: Int = if (knownIsEmpty) 0 else -1 + override def head: A = state.head override def tail: LazyList[A] = state.tail @inline private[this] def knownIsEmpty: Boolean = stateEvaluated && (isEmpty: @inline) - @inline private def knownNonEmpty: Boolean = stateEvaluated && !(isEmpty: @inline) - - // It's an imperfect world, but at least we can bottle up the - // imperfection in a capsule. - @inline private def asThat[That](x: AnyRef): That = x.asInstanceOf[That] - @inline private def isLLBuilder[B, That](bf: CanBuildFrom[LazyList[A], B, That]) = - bf(repr).isInstanceOf[LazyList.LazyBuilder[_]] - - override def companion: GenericCompanion[LazyList] = LazyList + @inline private def knownNonEmpty: Boolean = stateEvaluated && !(isEmpty: @inline) /** Evaluates all undefined elements of the lazy list. - * - * This method detects cycles in lazy lists, and terminates after all - * elements of the cycle are evaluated. For example: - * - * {{{ - * val ring: LazyList[Int] = 1 #:: 2 #:: 3 #:: ring - * ring.force - * ring.toString - * - * // prints - * // - * // LazyList(1, 2, 3, ...) - * }}} - * - * This method will *not* terminate for non-cyclic infinite-sized collections. - * - * @return this - */ + * + * This method detects cycles in lazy lists, and terminates after all + * elements of the cycle are evaluated. For example: + * + * {{{ + * val ring: LazyList[Int] = 1 #:: 2 #:: 3 #:: ring + * ring.force + * ring.toString + * + * // prints + * // + * // LazyList(1, 2, 3, ...) + * }}} + * + * This method will *not* terminate for non-cyclic infinite-sized collections. + * + * @return this + */ def force: this.type = { // Use standard 2x 1x iterator trick for cycle detection ("those" is slow one) var these, those: LazyList[A] = this @@ -308,25 +318,25 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St } /** @inheritdoc - * - * The iterator returned by this method preserves laziness; elements are - * only evaluated individually as needed. - */ + * + * The iterator returned by this method preserves laziness; elements are + * only evaluated individually as needed. + */ override def iterator: Iterator[A] = if (knownIsEmpty) Iterator.empty else new LazyIterator(this) /** Apply the given function `f` to each element of this linear sequence - * (while respecting the order of the elements). - * - * @param f The treatment to apply to each element. - * @note Overridden here as final to trigger tail-call optimization, which - * replaces 'this' with 'tail' at each iteration. This is absolutely - * necessary for allowing the GC to collect the underlying LazyList as elements - * are consumed. - * @note This function will force the realization of the entire LazyList - * unless the `f` throws an exception. - */ + * (while respecting the order of the elements). + * + * @param f The treatment to apply to each element. + * @note Overridden here as final to trigger tail-call optimization, which + * replaces 'this' with 'tail' at each iteration. This is absolutely + * necessary for allowing the GC to collect the underlying LazyList as elements + * are consumed. + * @note This function will force the realization of the entire LazyList + * unless the `f` throws an exception. + */ @tailrec override def foreach[U](f: A => U): Unit = { if (!isEmpty) { @@ -336,13 +346,13 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St } /** LazyList specialization of foldLeft which allows GC to collect along the - * way. - * - * @tparam B The type of value being accumulated. - * @param z The initial value seeded into the function `op`. - * @param op The operation to perform on successive elements of the `LazyList`. - * @return The accumulated value from successive applications of `op`. - */ + * way. + * + * @tparam B The type of value being accumulated. + * @param z The initial value seeded into the function `op`. + * @param op The operation to perform on successive elements of the `LazyList`. + * @return The accumulated value from successive applications of `op`. + */ @tailrec override def foldLeft[B](z: B)(op: (B, A) => B): B = if (isEmpty) z @@ -352,67 +362,54 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St protected[this] def writeReplace(): AnyRef = if (knownNonEmpty) new LazyList.SerializationProxy[A](this) else this - override def stringPrefix = "LazyList" + override protected[this] def className = "LazyList" /** The lazy list resulting from the concatenation of this lazy list with the argument lazy list. - * - * $preservesLaziness - * - * $appendStackSafety - * - * @param suffix The collection that gets appended to this lazy list - * @return The lazy list containing elements of this lazy list and the iterable object. - */ - def lazyAppendedAll[B >: A](suffix: => GenTraversableOnce[B]): LazyList[B] = + * + * $preservesLaziness + * + * $appendStackSafety + * + * @param suffix The collection that gets appended to this lazy list + * @return The lazy list containing elements of this lazy list and the iterable object. + */ + def lazyAppendedAll[B >: A](suffix: => collection.IterableOnce[B]): LazyList[B] = newLL { if (isEmpty) suffix match { - case lazyList: LazyList[B] => lazyList.state // don't recompute the LazyList - case coll => stateFromIterator(coll.toIterator) - } else sCons(head, tail lazyAppendedAll suffix) + case lazyList: LazyList[B] => lazyList.state // don't recompute the LazyList + case coll if coll.knownSize == 0 => State.Empty + case coll => stateFromIterator(coll.iterator) + } + else sCons(head, tail lazyAppendedAll suffix) } /** @inheritdoc - * - * $preservesLaziness - * - * $appendStackSafety - */ - override def ++[B >: A, That](suffix: GenTraversableOnce[B])( - implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat { - if (knownIsEmpty) LazyList.from(suffix) - else lazyAppendedAll(suffix) - } else super.++(suffix)(bf) + * + * $preservesLaziness + * + * $appendStackSafety + */ + override def appendedAll[B >: A](suffix: IterableOnce[B]): LazyList[B] = + if (knownIsEmpty) LazyList.from(suffix) + else lazyAppendedAll(suffix) /** @inheritdoc - * - * $preservesLaziness - * - * $appendStackSafety - */ - override def :+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat { - if (knownIsEmpty) newLL(sCons(elem, LazyList.empty)) - else lazyAppendedAll(Iterator.single(elem)) - } else super.:+(elem)(bf) + * + * $preservesLaziness + * + * $appendStackSafety + */ + override def appended[B >: A](elem: B): LazyList[B] = + if (knownIsEmpty) newLL(sCons(elem, LazyList.empty)) + else lazyAppendedAll(Iterator.single(elem)) /** @inheritdoc - * - * $evaluatesAllElements - */ - override def equals(that: Any): Boolean = - if (this eq that.asInstanceOf[AnyRef]) true else super.equals(that) - - /** @inheritdoc - * - * $preservesLaziness - */ - override def scanLeft[B, That](z: B)(op: (B, A) => B)( - implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat { - if (knownIsEmpty) newLL(sCons(z, LazyList.empty)) - else newLL(scanLeftState(z)(op)) - } else super.scanLeft(z)(op)(bf) + * + * $preservesLaziness + */ + override def scanLeft[B](z: B)(op: (B, A) => B): LazyList[B] = + if (knownIsEmpty) newLL(sCons(z, LazyList.empty)) + else newLL(scanLeftState(z)(op)) private def scanLeftState[B](z: B)(op: (B, A) => B): State[B] = sCons( @@ -424,16 +421,16 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St ) /** LazyList specialization of reduceLeft which allows GC to collect - * along the way. - * - * @tparam B The type of value being accumulated. - * @param f The operation to perform on successive elements of the `LazyList`. - * @return The accumulated value from successive applications of `f`. - */ + * along the way. + * + * @tparam B The type of value being accumulated. + * @param f The operation to perform on successive elements of the `LazyList`. + * @return The accumulated value from successive applications of `f`. + */ override def reduceLeft[B >: A](f: (B, A) => B): B = { if (this.isEmpty) throw new UnsupportedOperationException("empty.reduceLeft") else { - var reducedRes: B = this.head + var reducedRes: B = this.head var left: LazyList[A] = this.tail while (!left.isEmpty) { reducedRes = f(reducedRes, left.head) @@ -444,101 +441,75 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St } /** @inheritdoc - * - * $preservesLaziness - */ + * + * $preservesLaziness + */ override def partition(p: A => Boolean): (LazyList[A], LazyList[A]) = (filter(p), filterNot(p)) /** @inheritdoc - * - * $preservesLaziness - */ - def partitionMap[A1, A2](f: A => Either[A1, A2]): (LazyList[A1], LazyList[A2]) = { - val (left, right) = mapToLL(f).partition(_.isLeft) - (left.mapToLL(_.asInstanceOf[Left[A1, _]].a), right.mapToLL(_.asInstanceOf[Right[_, A2]].b)) + * + * $preservesLaziness + */ + override def partitionMap[A1, A2](f: A => Either[A1, A2]): (LazyList[A1], LazyList[A2]) = { + val (left, right) = map(f).partition(_.isLeft) + (left.map(_.asInstanceOf[Left[A1, _]].value), right.map(_.asInstanceOf[Right[_, A2]].value)) } /** @inheritdoc - * - * $preservesLaziness - */ + * + * $preservesLaziness + */ override def filter(pred: A => Boolean): LazyList[A] = if (knownIsEmpty) LazyList.empty else LazyList.filterImpl(this, pred, isFlipped = false) /** @inheritdoc - * - * $preservesLaziness - */ + * + * $preservesLaziness + */ override def filterNot(pred: A => Boolean): LazyList[A] = if (knownIsEmpty) LazyList.empty else LazyList.filterImpl(this, pred, isFlipped = true) /** A `collection.WithFilter` which allows GC of the head of lazy list during processing. - * - * This method is not particularly useful for a lazy list, as [[filter]] already preserves - * laziness. - * - * The `collection.WithFilter` returned by this method preserves laziness; elements are - * only evaluated individually as needed. - */ - override def withFilter(p: A => Boolean): FilterMonadic[A, LazyList[A]] = - new LazyList.WithFilter(this, p) + * + * This method is not particularly useful for a lazy list, as [[filter]] already preserves + * laziness. + * + * The `collection.WithFilter` returned by this method preserves laziness; elements are + * only evaluated individually as needed. + */ + override def withFilter(p: A => Boolean): collection.WithFilter[A, LazyList] = + new LazyList.WithFilter(coll, p) /** @inheritdoc - * - * $preservesLaziness - */ - override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat { - newLL(sCons(elem, this)) - } else super.+:(elem)(bf) + * + * $preservesLaziness + */ + override def prepended[B >: A](elem: B): LazyList[B] = newLL(sCons(elem, this)) /** @inheritdoc - * - * $preservesLaziness - */ - override def ++:[B >: A, That](prefix: TraversableOnce[B])( - implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat { - if (knownIsEmpty) LazyList.from(prefix) - else newLL(stateFromIteratorConcatSuffix(prefix.toIterator)(state)) - } else super.++:(prefix)(bf) - - /** @inheritdoc - * - * $preservesLaziness - */ - override def ++:[B >: A, That](prefix: Traversable[B])( - implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat { - if (knownIsEmpty) LazyList.from(prefix) - else newLL(stateFromIteratorConcatSuffix(prefix.toIterator)(state)) - } else super.++:(prefix)(bf) - - private def prependedAllToLL[B >: A](prefix: Traversable[B]): LazyList[B] = + * + * $preservesLaziness + */ + override def prependedAll[B >: A](prefix: collection.IterableOnce[B]): LazyList[B] = if (knownIsEmpty) LazyList.from(prefix) - else newLL(stateFromIteratorConcatSuffix(prefix.toIterator)(state)) + else if (prefix.knownSize == 0) this + else newLL(stateFromIteratorConcatSuffix(prefix.iterator)(state)) /** @inheritdoc - * - * $preservesLaziness - */ - override def map[B, That](f: A => B)(implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat(mapToLL(f): @inline) - else super.map(f)(bf) - - private def mapToLL[B](f: A => B): LazyList[B] = + * + * $preservesLaziness + */ + override def map[B](f: A => B): LazyList[B] = if (knownIsEmpty) LazyList.empty else (mapImpl(f): @inline) /** @inheritdoc - * - * $preservesLaziness - */ - def tapEach[U](f: A => U): LazyList[A] = mapToLL { a => - f(a); a - } + * + * $preservesLaziness + */ + override def tapEach[U](f: A => U): LazyList[A] = map { a => f(a); a } private def mapImpl[B](f: A => B): LazyList[B] = newLL { @@ -547,35 +518,32 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St } /** @inheritdoc - * - * $preservesLaziness - */ - override def collect[B, That](pf: PartialFunction[A, B])( - implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat { - if (knownIsEmpty) LazyList.empty - else LazyList.collectImpl(this, pf) - } else super.collect(pf)(bf) + * + * $preservesLaziness + */ + override def collect[B](pf: PartialFunction[A, B]): LazyList[B] = + if (knownIsEmpty) LazyList.empty + else LazyList.collectImpl(this, pf) /** @inheritdoc - * - * This method does not evaluate any elements further than - * the first element for which the partial function is defined. - */ + * + * This method does not evaluate any elements further than + * the first element for which the partial function is defined. + */ @tailrec override def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = if (isEmpty) None else { val res = pf.applyOrElse(head, LazyList.anyToMarker.asInstanceOf[A => B]) - if (res.asInstanceOf[AnyRef] eq LazyList.pfMarker) tail.collectFirst(pf) + if (res.asInstanceOf[AnyRef] eq Statics.pfMarker) tail.collectFirst(pf) else Some(res) } /** @inheritdoc - * - * This method does not evaluate any elements further than - * the first element matching the predicate. - */ + * + * This method does not evaluate any elements further than + * the first element matching the predicate. + */ @tailrec override def find(p: A => Boolean): Option[A] = if (isEmpty) None @@ -586,129 +554,125 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St } /** @inheritdoc - * - * $preservesLaziness - */ - override def flatMap[B, That](f: A => GenTraversableOnce[B])( - implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat(flatMapToLL(f): @inline) - else super.flatMap(f)(bf) - - /** @inheritdoc - * - * $preservesLaziness - */ - override def flatten[B](implicit asIterable: A => GenTraversableOnce[B]): LazyList[B] = - flatMapToLL(asIterable) - - private def flatMapToLL[B](f: A => GenTraversableOnce[B]): LazyList[B] = + * + * $preservesLaziness + */ + // optimisations are not for speed, but for functionality + // see tickets #153, #498, #2147, and corresponding tests in run/ (as well as run/stream_flatmap_odds.scala) + override def flatMap[B](f: A => IterableOnce[B]): LazyList[B] = if (knownIsEmpty) LazyList.empty else LazyList.flatMapImpl(this, f) /** @inheritdoc - * - * $preservesLaziness - */ - override def zip[A1 >: A, B, That](that: GenIterable[B])( - implicit bf: CanBuildFrom[LazyList[A], (A1, B), That]): That = - if (isLLBuilder(bf)) asThat(zipToLL(that): @inline) - else super.zip(that)(bf) + * + * $preservesLaziness + */ + override def flatten[B](implicit asIterable: A => IterableOnce[B]): LazyList[B] = flatMap(asIterable) - private def zipToLL[B](that: GenIterable[B]): LazyList[(A, B)] = - if (this.knownIsEmpty) LazyList.empty - else newLL(zipState(that.toIterator)) + /** @inheritdoc + * + * $preservesLaziness + */ + override def zip[B](that: collection.IterableOnce[B]): LazyList[(A, B)] = + if (this.knownIsEmpty || that.knownSize == 0) LazyList.empty + else newLL(zipState(that.iterator)) private def zipState[B](it: Iterator[B]): State[(A, B)] = if (this.isEmpty || !it.hasNext) State.Empty else sCons((head, it.next()), newLL { tail zipState it }) /** @inheritdoc - * - * $preservesLaziness - */ - override def zipWithIndex[A1 >: A, That]( - implicit bf: CanBuildFrom[LazyList[A], (A1, Int), That]): That = - if (isLLBuilder(bf)) asThat { - this zip LazyList.from(0) - } else super.zipWithIndex(bf) + * + * $preservesLaziness + */ + override def zipWithIndex: LazyList[(A, Int)] = this zip LazyList.from(0) /** @inheritdoc - * - * $preservesLaziness - */ - override def zipAll[B, A1 >: A, That](that: GenIterable[B], thisElem: A1, thatElem: B)( - implicit bf: CanBuildFrom[LazyList[A], (A1, B), That]): That = - if (isLLBuilder(bf)) asThat { - if (this.knownIsEmpty) LazyList.continually(thisElem) zip that - else newLL(zipAllState(that.toIterator, thisElem, thatElem)) - } else super.zipAll(that, thisElem, thatElem)(bf) - - private def zipAllState[A1 >: A, B](it: Iterator[B], - thisElem: A1, - thatElem: B): State[(A1, B)] = { + * + * $preservesLaziness + */ + override def zipAll[A1 >: A, B](that: collection.Iterable[B], thisElem: A1, thatElem: B): LazyList[(A1, B)] = { + if (this.knownIsEmpty) { + if (that.knownSize == 0) LazyList.empty + else LazyList.continually(thisElem) zip that + } else { + if (that.knownSize == 0) zip(LazyList.continually(thatElem)) + else newLL(zipAllState(that.iterator, thisElem, thatElem)) + } + } + + private def zipAllState[A1 >: A, B](it: Iterator[B], thisElem: A1, thatElem: B): State[(A1, B)] = { if (it.hasNext) { - if (this.isEmpty) sCons((thisElem, it.next()), newLL { - LazyList.continually(thisElem) zipState it - }) + if (this.isEmpty) sCons((thisElem, it.next()), newLL { LazyList.continually(thisElem) zipState it }) else sCons((this.head, it.next()), newLL { this.tail.zipAllState(it, thisElem, thatElem) }) } else { if (this.isEmpty) State.Empty - else sCons((this.head, thatElem), this.tail zipToLL LazyList.continually(thatElem)) + else sCons((this.head, thatElem), this.tail zip LazyList.continually(thatElem)) } } /** @inheritdoc - * - * $preservesLaziness - */ + * + * This method is not particularly useful for a lazy list, as [[zip]] already preserves + * laziness. + * + * The `collection.LazyZip2` returned by this method preserves laziness; elements are + * only evaluated individually as needed. + */ + // just in case it can be meaningfully overridden at some point + override def lazyZip[B](that: collection.Iterable[B]): LazyZip2[A, B, LazyList.this.type] = + super.lazyZip(that) + + /** @inheritdoc + * + * $preservesLaziness + */ override def unzip[A1, A2](implicit asPair: A => (A1, A2)): (LazyList[A1], LazyList[A2]) = - (mapToLL(asPair(_)._1), mapToLL(asPair(_)._2)) + (map(asPair(_)._1), map(asPair(_)._2)) /** @inheritdoc - * - * $preservesLaziness - */ - override def unzip3[A1, A2, A3]( - implicit asTriple: A => (A1, A2, A3)): (LazyList[A1], LazyList[A2], LazyList[A3]) = - (mapToLL(asTriple(_)._1), mapToLL(asTriple(_)._2), mapToLL(asTriple(_)._3)) + * + * $preservesLaziness + */ + override def unzip3[A1, A2, A3](implicit asTriple: A => (A1, A2, A3)): (LazyList[A1], LazyList[A2], LazyList[A3]) = + (map(asTriple(_)._1), map(asTriple(_)._2), map(asTriple(_)._3)) /** @inheritdoc - * - * $initiallyLazy - * Additionally, it preserves laziness for all except the first `n` elements. - */ + * + * $initiallyLazy + * Additionally, it preserves laziness for all except the first `n` elements. + */ override def drop(n: Int): LazyList[A] = if (n <= 0) this else if (knownIsEmpty) LazyList.empty else LazyList.dropImpl(this, n) /** @inheritdoc - * - * $initiallyLazy - * Additionally, it preserves laziness for all elements after the predicate returns `false`. - */ + * + * $initiallyLazy + * Additionally, it preserves laziness for all elements after the predicate returns `false`. + */ override def dropWhile(p: A => Boolean): LazyList[A] = if (knownIsEmpty) LazyList.empty else LazyList.dropWhileImpl(this, p) /** @inheritdoc - * - * $initiallyLazy - */ + * + * $initiallyLazy + */ override def dropRight(n: Int): LazyList[A] = { if (n <= 0) this else if (knownIsEmpty) LazyList.empty - else - newLL { - var scout = this - var remaining = n - // advance scout n elements ahead (or until empty) - while (remaining > 0 && !scout.isEmpty) { - remaining -= 1 - scout = scout.tail - } - dropRightState(scout) + else newLL { + var scout = this + var remaining = n + // advance scout n elements ahead (or until empty) + while (remaining > 0 && !scout.isEmpty) { + remaining -= 1 + scout = scout.tail } + dropRightState(scout) + } } private def dropRightState(scout: LazyList[_]): State[A] = @@ -716,26 +680,25 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St else sCons(head, newLL(tail.dropRightState(scout.tail))) /** @inheritdoc - * - * $preservesLaziness - */ + * + * $preservesLaziness + */ override def take(n: Int): LazyList[A] = if (knownIsEmpty) LazyList.empty else (takeImpl(n): @inline) private def takeImpl(n: Int): LazyList[A] = { if (n <= 0) LazyList.empty - else - newLL { - if (isEmpty) State.Empty - else sCons(head, tail.takeImpl(n - 1)) - } + else newLL { + if (isEmpty) State.Empty + else sCons(head, tail.takeImpl(n - 1)) + } } /** @inheritdoc - * - * $preservesLaziness - */ + * + * $preservesLaziness + */ override def takeWhile(p: A => Boolean): LazyList[A] = if (knownIsEmpty) LazyList.empty else (takeWhileImpl(p): @inline) @@ -747,24 +710,24 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St } /** @inheritdoc - * - * $initiallyLazy - */ + * + * $initiallyLazy + */ override def takeRight(n: Int): LazyList[A] = if (n <= 0 || knownIsEmpty) LazyList.empty else LazyList.takeRightImpl(this, n) /** @inheritdoc - * - * $initiallyLazy - * Additionally, it preserves laziness for all but the first `from` elements. - */ + * + * $initiallyLazy + * Additionally, it preserves laziness for all but the first `from` elements. + */ override def slice(from: Int, until: Int): LazyList[A] = take(until).drop(from) /** @inheritdoc - * - * $evaluatesAllElements - */ + * + * $evaluatesAllElements + */ override def reverse: LazyList[A] = reverseOnto(LazyList.empty) // need contravariant type B to make the compiler happy - still returns LazyList[A] @@ -773,50 +736,21 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St if (isEmpty) tl else tail.reverseOnto(newLL(sCons(head, tl))) - private def occCounts0[B](sq: collection.Seq[B]): mutable.Map[B, Int] = { - val occ = new mutable.HashMap[B, Int] { override def default(k: B) = 0 } - for (y <- sq) occ(y) += 1 - occ - } - /** @inheritdoc - * - * $preservesLaziness - */ - override def diff[B >: A](that: GenSeq[B]): LazyList[A] = + * + * $preservesLaziness + */ + override def diff[B >: A](that: collection.Seq[B]): LazyList[A] = if (knownIsEmpty) LazyList.empty - else { - val occ = occCounts0(that.seq) - LazyList.from { - iterator.filter { x => - val ox = occ(x) // Avoid multiple map lookups - if (ox == 0) true - else { - occ(x) = ox - 1 - false - } - } - } - } + else super.diff(that) /** @inheritdoc - * - * $preservesLaziness - */ - override def intersect[B >: A](that: GenSeq[B]): LazyList[A] = + * + * $preservesLaziness + */ + override def intersect[B >: A](that: collection.Seq[B]): LazyList[A] = if (knownIsEmpty) LazyList.empty - else { - val occ = occCounts0(that.seq) - LazyList.from { - iterator.filter { x => - val ox = occ(x) // Avoid multiple map lookups - if (ox > 0) { - occ(x) = ox - 1 - true - } else false - } - } - } + else super.intersect(that) @tailrec private def lengthGt(len: Int): Boolean = @@ -825,20 +759,20 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St else tail.lengthGt(len - 1) /** @inheritdoc - * - * The iterator returned by this method mostly preserves laziness; - * a single element ahead of the iterator is evaluated. - */ + * + * The iterator returned by this method mostly preserves laziness; + * a single element ahead of the iterator is evaluated. + */ override def grouped(size: Int): Iterator[LazyList[A]] = { require(size > 0, "size must be positive, but was " + size) slidingImpl(size = size, step = size) } /** @inheritdoc - * - * The iterator returned by this method mostly preserves laziness; - * `size - step max 1` elements ahead of the iterator are evaluated. - */ + * + * The iterator returned by this method mostly preserves laziness; + * `size - step max 1` elements ahead of the iterator are evaluated. + */ override def sliding(size: Int, step: Int): Iterator[LazyList[A]] = { require(size > 0 && step > 0, s"size=$size and step=$step, but both must be positive") slidingImpl(size = size, step = step) @@ -849,51 +783,46 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St else new SlidingIterator[A](this, size = size, step = step) /** @inheritdoc - * - * $preservesLaziness - */ - override def padTo[B >: A, That](len: Int, elem: B)( - implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat(padToLL(len, elem)) - else super.padTo(len, elem)(bf) - - private def padToLL[B >: A](len: Int, elem: B): LazyList[B] = + * + * $preservesLaziness + */ + override def padTo[B >: A](len: Int, elem: B): LazyList[B] = { if (len <= 0) this - else - newLL { - if (isEmpty) LazyList.fill(len)(elem).state - else sCons(head, tail.padToLL(len - 1, elem)) - } + else newLL { + if (isEmpty) LazyList.fill(len)(elem).state + else sCons(head, tail.padTo(len - 1, elem)) + } + } /** @inheritdoc - * - * $preservesLaziness - */ - override def patch[B >: A, That](from: Int, other: GenSeq[B], replaced: Int)( - implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat { - if (knownIsEmpty) LazyList from other - else patchImpl(from, other, replaced) - } else super.patch(from, other, replaced) - - private def patchImpl[B >: A](from: Int, other: GenSeq[B], replaced: Int): LazyList[B] = + * + * $preservesLaziness + */ + override def patch[B >: A](from: Int, other: IterableOnce[B], replaced: Int): LazyList[B] = + if (knownIsEmpty) LazyList from other + else patchImpl(from, other, replaced) + + private def patchImpl[B >: A](from: Int, other: IterableOnce[B], replaced: Int): LazyList[B] = newLL { - if (from <= 0) - stateFromIteratorConcatSuffix(other.toIterator)(LazyList.dropImpl(this, replaced).state) - else if (isEmpty) stateFromIterator(other.toIterator) + if (from <= 0) stateFromIteratorConcatSuffix(other.iterator)(LazyList.dropImpl(this, replaced).state) + else if (isEmpty) stateFromIterator(other.iterator) else sCons(head, tail.patchImpl(from - 1, other, replaced)) } /** @inheritdoc - * - * $preservesLaziness - */ - override def updated[B >: A, That](index: Int, elem: B)( - implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - if (isLLBuilder(bf)) asThat { - if (index < 0) throw new IndexOutOfBoundsException(s"$index") - else updatedImpl(index, elem, index) - } else super.updated(index, elem) + * + * $evaluatesAllElements + */ + // overridden just in case a lazy implementation is developed at some point + override def transpose[B](implicit asIterable: A => collection.Iterable[B]): LazyList[LazyList[B]] = super.transpose + + /** @inheritdoc + * + * $preservesLaziness + */ + override def updated[B >: A](index: Int, elem: B): LazyList[B] = + if (index < 0) throw new IndexOutOfBoundsException(s"$index") + else updatedImpl(index, elem, index) private def updatedImpl[B >: A](index: Int, elem: B, startIndex: Int): LazyList[B] = { newLL { @@ -904,41 +833,35 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St } /** Appends all elements of this $coll to a string builder using start, end, and separator strings. - * The written text begins with the string `start` and ends with the string `end`. - * Inside, the string representations (w.r.t. the method `toString`) - * of all elements of this $coll are separated by the string `sep`. - * - * An undefined state is represented with `"<not computed>"` and cycles are represented with `"<cycle>"`. - * - * $evaluatesAllElements - * - * @param sb the string builder to which elements are appended. - * @param start the starting string. - * @param sep the separator string. - * @param end the ending string. - * @return the string builder `b` to which elements were appended. - */ - override def addString(sb: StringBuilder, - start: String, - sep: String, - end: String): StringBuilder = { + * The written text begins with the string `start` and ends with the string `end`. + * Inside, the string representations (w.r.t. the method `toString`) + * of all elements of this $coll are separated by the string `sep`. + * + * An undefined state is represented with `"<not computed>"` and cycles are represented with `"<cycle>"`. + * + * $evaluatesAllElements + * + * @param sb the string builder to which elements are appended. + * @param start the starting string. + * @param sep the separator string. + * @param end the ending string. + * @return the string builder `b` to which elements were appended. + */ + override def addString(sb: StringBuilder, start: String, sep: String, end: String): StringBuilder = { force - addStringNoForce(sb, start, sep, end) + addStringNoForce(sb.underlying, start, sep, end) sb } - private[this] def addStringNoForce(b: StringBuilder, - start: String, - sep: String, - end: String): StringBuilder = { + private[this] def addStringNoForce(b: JStringBuilder, start: String, sep: String, end: String): JStringBuilder = { b.append(start) if (!stateDefined) b.append("") else if (!isEmpty) { b.append(head) - var cursor = this + var cursor = this @inline def appendCursorElement(): Unit = b.append(sep).append(cursor.head) - var scout = tail - @inline def scoutNonEmpty: Boolean = scout.stateDefined && !scout.isEmpty + var scout = tail + @inline def scoutNonEmpty: Boolean = scout.stateDefined && !scout.isEmpty if ((cursor ne scout) && (!scout.stateDefined || (cursor.state ne scout.state))) { cursor = scout if (scoutNonEmpty) { @@ -952,7 +875,7 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St } } } - if (!scoutNonEmpty) { // Not a cycle, scout hit an end + if (!scoutNonEmpty) { // Not a cycle, scout hit an end while (cursor ne scout) { appendCursorElement() cursor = cursor.tail @@ -971,7 +894,7 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St // starts at the beginning of the prefix, they'll collide exactly at // the start of the loop. var runner = this - var k = 0 + var k = 0 while (!same(runner, scout)) { runner = runner.tail scout = scout.tail @@ -997,24 +920,24 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St } /** $preservesLaziness - * - * @return a string representation of this collection. An undefined state is - * represented with `"<not computed>"` and cycles are represented with `"<cycle>"` - * - * Examples: - * - * - `"LazyList(4, <not computed>)"`, a non-empty lazy list ; - * - `"LazyList(1, 2, 3, <not computed>)"`, a lazy list with at least three elements ; - * - `"LazyList(1, 2, 3, <cycle>)"`, an infinite lazy list that contains - * a cycle at the fourth element. - */ - override def toString(): String = - addStringNoForce(new StringBuilder(stringPrefix), "(", ", ", ")").toString + * + * @return a string representation of this collection. An undefined state is + * represented with `"<not computed>"` and cycles are represented with `"<cycle>"` + * + * Examples: + * + * - `"LazyList(4, <not computed>)"`, a non-empty lazy list ; + * - `"LazyList(1, 2, 3, <not computed>)"`, a lazy list with at least three elements ; + * - `"LazyList(1, 2, 3, <cycle>)"`, an infinite lazy list that contains + * a cycle at the fourth element. + */ + override def toString(): String = addStringNoForce(new JStringBuilder(className), "(", ", ", ")").toString /** @inheritdoc - * - * $preservesLaziness - */ + * + * $preservesLaziness + */ + @deprecated("Check .knownSize instead of .hasDefiniteSize for more actionable information (see scaladoc for details)", "2.13.0") override def hasDefiniteSize: Boolean = { if (!stateDefined) false else if (isEmpty) true @@ -1032,75 +955,16 @@ final class LazyList[+A] private (private[this] var lazyState: () => LazyList.St if (those eq these) return false those = those.tail } - false // Cycle detected + false // Cycle detected } } - - override def sameElements[B >: A](that: GenIterable[B]): Boolean = that match { - case that: LazyList[B] => this eqLL that - case _ => super.sameElements(that) - } - - @tailrec - private def eqLL[B >: A](that: LazyList[B]): Boolean = - (this eq that) || - (this.state eq that.state) || - (!this.isEmpty && - !that.isEmpty && - (this.head == that.head) && - (this.tail eqLL that.tail)) - - override def splitAt(n: Int): (LazyList[A], LazyList[A]) = (take(n), drop(n)) - - override def span(p: A => Boolean): (LazyList[A], LazyList[A]) = (takeWhile(p), dropWhile(p)) - - override def distinct: LazyList[A] = distinctBy(identity) - - def distinctBy[B](f: A => B): LazyList[A] = - if (knownIsEmpty) LazyList.empty - else - LazyList.from { - val outer = iterator - new AbstractIterator[A] { - private[this] val traversedValues = mutable.HashSet.empty[B] - private[this] var nextElementDefined: Boolean = false - private[this] var nextElement: A = _ - - def hasNext: Boolean = - nextElementDefined || (outer.hasNext && { - val a = outer.next() - if (traversedValues.add(f(a))) { - nextElement = a - nextElementDefined = true - true - } else hasNext - }) - - def next(): A = - if (hasNext) { - nextElementDefined = false - nextElement - } else { - Iterator.empty.next() - } - } - } - - override def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = - if (cbf().isInstanceOf[LazyList.LazyBuilder[_]]) asThat(this) - else super.to(cbf) - - override def init: LazyList[A] = { - if (isEmpty) throw new UnsupportedOperationException - dropRight(1) - } } /** - * $factoryInfo - * @define coll lazy list - * @define Coll `LazyList` - */ + * $factoryInfo + * @define coll lazy list + * @define Coll `LazyList` + */ @SerialVersionUID(3L) object LazyList extends SeqFactory[LazyList] { // Eagerly evaluate cached empty instance @@ -1115,8 +979,7 @@ object LazyList extends SeqFactory[LazyList] { @SerialVersionUID(3L) object Empty extends State[Nothing] { def head: Nothing = throw new NoSuchElementException("head of empty lazy list") - def tail: LazyList[Nothing] = - throw new UnsupportedOperationException("tail of empty lazy list") + def tail: LazyList[Nothing] = throw new UnsupportedOperationException("tail of empty lazy list") } @SerialVersionUID(3L) @@ -1129,8 +992,7 @@ object LazyList extends SeqFactory[LazyList] { /** Creates a new State.Cons. */ @inline private def sCons[A](hd: A, tl: LazyList[A]): State[A] = new State.Cons[A](hd, tl) - private val pfMarker: AnyRef = new AnyRef - private val anyToMarker: Any => Any = _ => pfMarker + private val anyToMarker: Any => Any = _ => Statics.pfMarker /* All of the following `Impl` methods are carefully written so as not to * leak the beginning of the `LazyList`. They copy the initial `LazyList` (`ll`) into @@ -1142,16 +1004,16 @@ object LazyList extends SeqFactory[LazyList] { private def filterImpl[A](ll: LazyList[A], p: A => Boolean, isFlipped: Boolean): LazyList[A] = { // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD - var restRef = ll // val restRef = new ObjectRef(ll) + var restRef = ll // val restRef = new ObjectRef(ll) newLL { var elem: A = null.asInstanceOf[A] var found = false - var rest = restRef // var rest = restRef.elem + var rest = restRef // var rest = restRef.elem while (!found && !rest.isEmpty) { - elem = rest.head - found = p(elem) != isFlipped - rest = rest.tail - restRef = rest // restRef.elem = rest + elem = rest.head + found = p(elem) != isFlipped + rest = rest.tail + restRef = rest // restRef.elem = rest } if (found) sCons(elem, filterImpl(rest, p, isFlipped)) else State.Empty } @@ -1159,42 +1021,42 @@ object LazyList extends SeqFactory[LazyList] { private def collectImpl[A, B](ll: LazyList[A], pf: PartialFunction[A, B]): LazyList[B] = { // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD - var restRef = ll // val restRef = new ObjectRef(ll) + var restRef = ll // val restRef = new ObjectRef(ll) newLL { - val marker = pfMarker + val marker = Statics.pfMarker val toMarker = anyToMarker.asInstanceOf[A => B] // safe because Function1 is erased - var res: B = marker.asInstanceOf[B] // safe because B is unbounded - var rest = restRef // var rest = restRef.elem - while ((res.asInstanceOf[AnyRef] eq marker) && !rest.isEmpty) { - res = pf.applyOrElse(rest.head, toMarker) - rest = rest.tail - restRef = rest // restRef.elem = rest + var res: B = marker.asInstanceOf[B] // safe because B is unbounded + var rest = restRef // var rest = restRef.elem + while((res.asInstanceOf[AnyRef] eq marker) && !rest.isEmpty) { + res = pf.applyOrElse(rest.head, toMarker) + rest = rest.tail + restRef = rest // restRef.elem = rest } if (res.asInstanceOf[AnyRef] eq marker) State.Empty else sCons(res, collectImpl(rest, pf)) } } - private def flatMapImpl[A, B](ll: LazyList[A], f: A => GenTraversableOnce[B]): LazyList[B] = { + private def flatMapImpl[A, B](ll: LazyList[A], f: A => IterableOnce[B]): LazyList[B] = { // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD - var restRef = ll // val restRef = new ObjectRef(ll) + var restRef = ll // val restRef = new ObjectRef(ll) newLL { var it: Iterator[B] = null var itHasNext = false - var rest = restRef // var rest = restRef.elem + var rest = restRef // var rest = restRef.elem while (!itHasNext && !rest.isEmpty) { - it = f(rest.head).toIterator + it = f(rest.head).iterator itHasNext = it.hasNext - if (!itHasNext) { // wait to advance `rest` because `it.next()` can throw - rest = rest.tail - restRef = rest // restRef.elem = rest + if (!itHasNext) { // wait to advance `rest` because `it.next()` can throw + rest = rest.tail + restRef = rest // restRef.elem = rest } } if (itHasNext) { val head = it.next() - rest = rest.tail - restRef = rest // restRef.elem = rest + rest = rest.tail + restRef = rest // restRef.elem = rest sCons(head, newLL(stateFromIteratorConcatSuffix(it)(flatMapImpl(rest, f).state))) } else State.Empty } @@ -1202,16 +1064,16 @@ object LazyList extends SeqFactory[LazyList] { private def dropImpl[A](ll: LazyList[A], n: Int): LazyList[A] = { // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD - var restRef = ll // val restRef = new ObjectRef(ll) - var iRef = n // val iRef = new IntRef(n) + var restRef = ll // val restRef = new ObjectRef(ll) + var iRef = n // val iRef = new IntRef(n) newLL { - var rest = restRef // var rest = restRef.elem - var i = iRef // var i = iRef.elem + var rest = restRef // var rest = restRef.elem + var i = iRef // var i = iRef.elem while (i > 0 && !rest.isEmpty) { - rest = rest.tail - restRef = rest // restRef.elem = rest - i -= 1 - iRef = i // iRef.elem = i + rest = rest.tail + restRef = rest // restRef.elem = rest + i -= 1 + iRef = i // iRef.elem = i } rest.state } @@ -1219,12 +1081,12 @@ object LazyList extends SeqFactory[LazyList] { private def dropWhileImpl[A](ll: LazyList[A], p: A => Boolean): LazyList[A] = { // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD - var restRef = ll // val restRef = new ObjectRef(ll) + var restRef = ll // val restRef = new ObjectRef(ll) newLL { - var rest = restRef // var rest = restRef.elem + var rest = restRef // var rest = restRef.elem while (!rest.isEmpty && p(rest.head)) { - rest = rest.tail - restRef = rest // restRef.elem = rest + rest = rest.tail + restRef = rest // restRef.elem = rest } rest.state } @@ -1232,26 +1094,26 @@ object LazyList extends SeqFactory[LazyList] { private def takeRightImpl[A](ll: LazyList[A], n: Int): LazyList[A] = { // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD - var restRef = ll // val restRef = new ObjectRef(ll) - var scoutRef = ll // val scoutRef = new ObjectRef(ll) - var remainingRef = n // val remainingRef = new IntRef(n) + var restRef = ll // val restRef = new ObjectRef(ll) + var scoutRef = ll // val scoutRef = new ObjectRef(ll) + var remainingRef = n // val remainingRef = new IntRef(n) newLL { - var scout = scoutRef // var scout = scoutRef.elem - var remaining = remainingRef // var remaining = remainingRef.elem + var scout = scoutRef // var scout = scoutRef.elem + var remaining = remainingRef // var remaining = remainingRef.elem // advance `scout` `n` elements ahead (or until empty) while (remaining > 0 && !scout.isEmpty) { - scout = scout.tail - scoutRef = scout // scoutRef.elem = scout - remaining -= 1 - remainingRef = remaining // remainingRef.elem = remaining + scout = scout.tail + scoutRef = scout // scoutRef.elem = scout + remaining -= 1 + remainingRef = remaining // remainingRef.elem = remaining } - var rest = restRef // var rest = restRef.elem + var rest = restRef // var rest = restRef.elem // advance `rest` and `scout` in tandem until `scout` reaches the end - while (!scout.isEmpty) { - scout = scout.tail - scoutRef = scout // scoutRef.elem = scout - rest = rest.tail // can't throw an exception as `scout` has already evaluated its tail - restRef = rest // restRef.elem = rest + while(!scout.isEmpty) { + scout = scout.tail + scoutRef = scout // scoutRef.elem = scout + rest = rest.tail // can't throw an exception as `scout` has already evaluated its tail + restRef = rest // restRef.elem = rest } // `rest` is the last `n` elements (or all of them) rest.state @@ -1259,13 +1121,12 @@ object LazyList extends SeqFactory[LazyList] { } /** An alternative way of building and matching lazy lists using LazyList.cons(hd, tl). - */ + */ object cons { - /** A lazy list consisting of a given first element and remaining elements - * @param hd The first element of the result lazy list - * @param tl The remaining elements of the result lazy list - */ + * @param hd The first element of the result lazy list + * @param tl The remaining elements of the result lazy list + */ def apply[A](hd: => A, tl: => LazyList[A]): LazyList[A] = newLL(sCons(hd, newLL(tl.state))) /** Maps a lazy list to its head and tail */ @@ -1275,15 +1136,13 @@ object LazyList extends SeqFactory[LazyList] { implicit def toDeferrer[A](l: => LazyList[A]): Deferrer[A] = new Deferrer[A](() => l) final class Deferrer[A] private[LazyList] (private val l: () => LazyList[A]) extends AnyVal { - /** Construct a LazyList consisting of a given first element followed by elements - * from another LazyList. - */ - def #::[B >: A](elem: => B): LazyList[B] = newLL(sCons(elem, newLL(l().state))) - + * from another LazyList. + */ + def #:: [B >: A](elem: => B): LazyList[B] = newLL(sCons(elem, newLL(l().state))) /** Construct a LazyList consisting of the concatenation of the given LazyList and - * another LazyList. - */ + * another LazyList. + */ def #:::[B >: A](prefix: LazyList[B]): LazyList[B] = prefix lazyAppendedAll l() } @@ -1292,18 +1151,17 @@ object LazyList extends SeqFactory[LazyList] { if (!s.isEmpty) Some((s.head, s.tail)) else None } - def from[A](coll: GenTraversableOnce[A]): LazyList[A] = coll match { - case lazyList: LazyList[A] => lazyList - case _ => newLL(stateFromIterator(coll.toIterator)) + def from[A](coll: collection.IterableOnce[A]): LazyList[A] = coll match { + case lazyList: LazyList[A] => lazyList + case _ if coll.knownSize == 0 => empty[A] + case _ => newLL(stateFromIterator(coll.iterator)) } - override def apply[A](elems: A*): LazyList[A] = from(elems) - - override def empty[A]: LazyList[A] = _empty + def empty[A]: LazyList[A] = _empty /** Creates a State from an Iterator, with another State appended after the Iterator - * is empty. - */ + * is empty. + */ private def stateFromIteratorConcatSuffix[A](it: Iterator[A])(suffix: => State[A]): State[A] = if (it.hasNext) sCons(it.next(), newLL(stateFromIteratorConcatSuffix(it)(suffix))) else suffix @@ -1313,19 +1171,20 @@ object LazyList extends SeqFactory[LazyList] { if (it.hasNext) sCons(it.next(), newLL(stateFromIterator(it))) else State.Empty - def concat[A](xss: collection.Iterable[A]*): LazyList[A] = - newLL(concatIterator(xss.toIterator)) + override def concat[A](xss: collection.Iterable[A]*): LazyList[A] = + if (xss.knownSize == 0) empty + else newLL(concatIterator(xss.iterator)) private def concatIterator[A](it: Iterator[collection.Iterable[A]]): State[A] = if (!it.hasNext) State.Empty - else stateFromIteratorConcatSuffix(it.next().toIterator)(concatIterator(it)) + else stateFromIteratorConcatSuffix(it.next().iterator)(concatIterator(it)) /** An infinite LazyList that repeatedly applies a given function to a start value. - * - * @param start the start value of the LazyList - * @param f the function that's repeatedly applied - * @return the LazyList returning the infinite sequence of values `start, f(start), f(f(start)), ...` - */ + * + * @param start the start value of the LazyList + * @param f the function that's repeatedly applied + * @return the LazyList returning the infinite sequence of values `start, f(start), f(f(start)), ...` + */ def iterate[A](start: => A)(f: A => A): LazyList[A] = newLL { val head = start @@ -1333,31 +1192,31 @@ object LazyList extends SeqFactory[LazyList] { } /** - * Create an infinite LazyList starting at `start` and incrementing by - * step `step`. - * - * @param start the start value of the LazyList - * @param step the increment value of the LazyList - * @return the LazyList starting at value `start`. - */ + * Create an infinite LazyList starting at `start` and incrementing by + * step `step`. + * + * @param start the start value of the LazyList + * @param step the increment value of the LazyList + * @return the LazyList starting at value `start`. + */ def from(start: Int, step: Int): LazyList[Int] = newLL(sCons(start, from(start + step, step))) /** - * Create an infinite LazyList starting at `start` and incrementing by `1`. - * - * @param start the start value of the LazyList - * @return the LazyList starting at value `start`. - */ + * Create an infinite LazyList starting at `start` and incrementing by `1`. + * + * @param start the start value of the LazyList + * @return the LazyList starting at value `start`. + */ def from(start: Int): LazyList[Int] = from(start, 1) /** - * Create an infinite LazyList containing the given element expression (which - * is computed for each occurrence). - * - * @param elem the element composing the resulting LazyList - * @return the LazyList containing an infinite number of elem - */ + * Create an infinite LazyList containing the given element expression (which + * is computed for each occurrence). + * + * @param elem the element composing the resulting LazyList + * @return the LazyList containing an infinite number of elem + */ def continually[A](elem: => A): LazyList[A] = newLL(sCons(elem, continually(elem))) override def fill[A](n: Int)(elem: => A): LazyList[A] = @@ -1371,7 +1230,7 @@ object LazyList extends SeqFactory[LazyList] { } // significantly simpler than the iterator returned by Iterator.unfold - def unfold[A, S](init: S)(f: S => Option[(A, S)]): LazyList[A] = + override def unfold[A, S](init: S)(f: S => Option[(A, S)]): LazyList[A] = newLL { f(init) match { case Some((elem, state)) => sCons(elem, unfold(state)(f)) @@ -1380,15 +1239,14 @@ object LazyList extends SeqFactory[LazyList] { } /** The builder returned by this method only evaluates elements - * of collections added to it as needed. - * - * @tparam A the type of the ${coll}’s elements - * @return A builder for $Coll objects. - */ + * of collections added to it as needed. + * + * @tparam A the type of the ${coll}’s elements + * @return A builder for $Coll objects. + */ def newBuilder[A]: Builder[A, LazyList[A]] = new LazyBuilder[A] - private class LazyIterator[+A](private[this] var lazyList: LazyList[A]) - extends AbstractIterator[A] { + private class LazyIterator[+A](private[this] var lazyList: LazyList[A]) extends AbstractIterator[A] { override def hasNext: Boolean = !lazyList.isEmpty override def next(): A = @@ -1401,9 +1259,9 @@ object LazyList extends SeqFactory[LazyList] { } private class SlidingIterator[A](private[this] var lazyList: LazyList[A], size: Int, step: Int) - extends AbstractIterator[LazyList[A]] { + extends AbstractIterator[LazyList[A]] { private val minLen = size - step max 0 - private var first = true + private var first = true def hasNext: Boolean = if (first) !lazyList.isEmpty @@ -1420,26 +1278,20 @@ object LazyList extends SeqFactory[LazyList] { } } - private final class WithFilter[A] private[LazyList] (lazyList: LazyList[A], p: A => Boolean) - extends FilterMonadic[A, LazyList[A]] { + private final class WithFilter[A] private[LazyList](lazyList: LazyList[A], p: A => Boolean) + extends collection.WithFilter[A, LazyList] { private[this] val filtered = lazyList.filter(p) - def map[B, That](f: A => B)(implicit bf: CanBuildFrom[LazyList[A], B, That]): That = - filtered.map(f) - def flatMap[B, That](f: A => GenTraversableOnce[B])( - implicit bf: CanBuildFrom[LazyList[A], B, That]): That = filtered.flatMap(f) - def foreach[U](f: A => U): Unit = filtered.foreach(f) - def withFilter(q: A => Boolean): FilterMonadic[A, LazyList[A]] = new WithFilter(filtered, q) + def map[B](f: A => B): LazyList[B] = filtered.map(f) + def flatMap[B](f: A => IterableOnce[B]): LazyList[B] = filtered.flatMap(f) + def foreach[U](f: A => U): Unit = filtered.foreach(f) + def withFilter(q: A => Boolean): collection.WithFilter[A, LazyList] = new WithFilter(filtered, q) } - class LazyListCanBuildFrom[A] extends GenericCanBuildFrom[A] - - implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, LazyList[A]] = new LazyListCanBuildFrom[A] - - private final class LazyBuilder[A] extends Builder[A, LazyList[A]] { + private final class LazyBuilder[A] extends ReusableBuilder[A, LazyList[A]] { import LazyBuilder._ private[this] var next: DeferredState[A] = _ - private[this] var list: LazyList[A] = _ + private[this] var list: LazyList[A] = _ clear() @@ -1454,7 +1306,7 @@ object LazyList extends SeqFactory[LazyList] { list } - override def +=(elem: A): this.type = { + override def addOne(elem: A): this.type = { val deferred = new DeferredState[A] next init sCons(elem, newLL(deferred.eval())) next = deferred @@ -1462,10 +1314,12 @@ object LazyList extends SeqFactory[LazyList] { } // lazy implementation which doesn't evaluate the collection being added - override def ++=(xs: TraversableOnce[A]): this.type = { - val deferred = new DeferredState[A] - next init stateFromIteratorConcatSuffix(xs.toIterator)(deferred.eval()) - next = deferred + override def addAll(xs: IterableOnce[A]): this.type = { + if (xs.knownSize != 0) { + val deferred = new DeferredState[A] + next init stateFromIteratorConcatSuffix(xs.iterator)(deferred.eval()) + next = deferred + } this } } @@ -1488,21 +1342,18 @@ object LazyList extends SeqFactory[LazyList] { } } - private case object SerializeEnd - /** This serialization proxy is used for LazyLists which start with a sequence of evaluated cons cells. - * The forced sequence is serialized in a compact, sequential format, followed by the unevaluated tail, which uses - * standard Java serialization to store the complete structure of unevaluated thunks. This allows the serialization - * of long evaluated lazy lists without exhausting the stack through recursive serialization of cons cells. - */ + * The forced sequence is serialized in a compact, sequential format, followed by the unevaluated tail, which uses + * standard Java serialization to store the complete structure of unevaluated thunks. This allows the serialization + * of long evaluated lazy lists without exhausting the stack through recursive serialization of cons cells. + */ @SerialVersionUID(3L) - final class SerializationProxy[A](@transient protected var coll: LazyList[A]) - extends Serializable { + final class SerializationProxy[A](@transient protected var coll: LazyList[A]) extends Serializable { private[this] def writeObject(out: ObjectOutputStream): Unit = { out.defaultWriteObject() var these = coll - while (these.knownNonEmpty) { + while(these.knownNonEmpty) { out.writeObject(these.head) these = these.tail } @@ -1512,25 +1363,16 @@ object LazyList extends SeqFactory[LazyList] { private[this] def readObject(in: ObjectInputStream): Unit = { in.defaultReadObject() - val init = new ArrayBuffer[A] + val init = new ArrayBuffer[A] var initRead = false while (!initRead) in.readObject match { case SerializeEnd => initRead = true - case a => init += a.asInstanceOf[A] + case a => init += a.asInstanceOf[A] } val tail = in.readObject().asInstanceOf[LazyList[A]] - coll = tail.prependedAllToLL(init) + coll = init ++: tail } private[this] def readResolve(): Any = coll } - - override def iterate[A](start: A, len: Int)(f: A => A): LazyList[A] = - iterate(start)(f).take(len) - - override def range[A: Integral](start: A, end: A): LazyList[A] = - from(NumericRange(start, end, implicitly[Integral[A]].one)) - - override def range[A: Integral](start: A, end: A, step: A): LazyList[A] = - from(NumericRange(start, end, step)) } diff --git a/compat/src/test/scala/test/scala/collection/LazyListLazinessTest.scala b/compat/src/test/scala/test/scala/collection/LazyListLazinessTest.scala index 08cdaaf2..3d5f3802 100644 --- a/compat/src/test/scala/test/scala/collection/LazyListLazinessTest.scala +++ b/compat/src/test/scala/test/scala/collection/LazyListLazinessTest.scala @@ -2,11 +2,8 @@ package test.scala.collection import org.junit.Test import org.junit.Assert._ -import org.junit.Ignore -import scala.collection.compat.immutable.LazyList -import scala.collection.compat._ -import scala.collection.Iterator +import scala.collection.{IterableOnce, Iterator, SeqFactory} class LazyListLazinessTest { import LazyListLazinessTest._ @@ -214,7 +211,7 @@ class LazyListLazinessTest { genericAppendedColl_properlyLazy(append) val ll = LazyList.from(1) assertKnownEmptyYields(append(_, ll))(_ eq ll) - // assertKnownEmptyYieldsKnownEmpty(append(_, Nil)) + assertKnownEmptyYieldsKnownEmpty(append(_, Nil)) } @Test @@ -222,11 +219,19 @@ class LazyListLazinessTest { genericAppendedColl_properlyLazy(_ lazyAppendedAll _) } + @Test + def appendedAll_properlyLazy(): Unit = { + genericAppendedCollValue_properlyLazy(_ appendedAll _) + genericAppendedCollValue_properlyLazy(_ :++ _) + } + @Test def concat_properlyLazy(): Unit = { + genericAppendedCollValue_properlyLazy(_ concat _) genericAppendedCollValue_properlyLazy(_ ++ _) } + @deprecated("Uses deprecated union", since = "2.13.0") @Test def union_properlyLazy(): Unit = { genericAppendedCollValue_properlyLazy(_ union _) @@ -234,14 +239,14 @@ class LazyListLazinessTest { @Test def appended_properlyLazy(): Unit = { - val op = lazyListOp(_ :+ -1) + val op = lazyListOp(_ appended -1) assertLazyAll(op) assertRepeatedlyLazy(op) } @Test def prepended_properlyLazy(): Unit = { - val op = lazyListOp(ll => (-1 +: ll).tail) + val op = lazyListOp(_.prepended(-1).tail) assertLazyAll(op) assertRepeatedlyLazy(op) } @@ -250,7 +255,7 @@ class LazyListLazinessTest { def prependedAll_properlyLazy(): Unit = { def check(prefix: Seq[Int]): Unit = { val op = lazyListOp { ll => - var prepended = prefix ++: ll + var prepended = ll prependedAll prefix var toDrop = prefix.length while (toDrop > 0) { prepended = prepended.tail @@ -262,11 +267,14 @@ class LazyListLazinessTest { assertRepeatedlyLazy(op) } for (coll <- Seq(Nil, 2 :: Nil, 1 to 10)) check(coll) - // assertKnownEmptyYieldsKnownEmpty(Nil ++: _) + assertKnownEmptyYieldsKnownEmpty(_ prependedAll Nil) // Check that prepending a `LazyList` leaves it fully lazy val checker = new OpLazinessChecker - checker.lazyList ++: LazyList.from(0).take(4) + LazyList + .from(0) + .take(4) + .prependedAll(checker.lazyList) checker.assertAll(evaluated = false) } @@ -337,28 +345,19 @@ class LazyListLazinessTest { @Test def sizeCompare_properlyLazy(): Unit = { - for (factory <- List[Factory[Int, Seq[Int]]](LazyList, Vector)) { - // assertLazyAllSkipping(_ sizeCompare factory.fill(3)(1), 4) - assertLazyAllSkipping(_ sizeCompare factory.fromSpecific(List(1, 1, 1)), 4) + for (factory <- List[SeqFactory[Seq]](LazyList, Vector)) { + assertLazyAllSkipping(_ sizeCompare factory.fill(3)(1), 4) } } @Test def iterator_properlyLazy(): Unit = { - // genericLazyOp_properlyLazy(_.iterator to LazyList) - val op = lazyListOp(_.iterator to LazyList) - assertLazyAll(op) - assertRepeatedlyLazy(op) - // assertKnownEmptyYieldsKnownEmpty(op) + genericLazyOp_properlyLazy(_.iterator to LazyList) } @Test def view_properlyLazy(): Unit = { - // genericLazyOp_properlyLazy(_.view to LazyList) - val op = lazyListOp(_.view to LazyList) - assertLazyAll(op) - assertRepeatedlyLazy(op) - // assertKnownEmptyYieldsKnownEmpty(op) + genericLazyOp_properlyLazy(_.view to LazyList) } @Test @@ -385,13 +384,13 @@ class LazyListLazinessTest { @Test def corresponds_properlyLazy(): Unit = { - // assertLazyAllSkipping(_.corresponds(Iterator.empty[Int])(_ == _), 1) + assertLazyAllSkipping(_.corresponds(Iterator.empty[Int])(_ == _), 1) assertLazyAllSkipping(_.corresponds(LazyList.empty[Int])(_ == _), 1) - // assertLazyAllSkipping(_.corresponds(Iterator.from(1))(_ == _), 1) + assertLazyAllSkipping(_.corresponds(Iterator.from(1))(_ == _), 1) assertLazyAllSkipping(_.corresponds(LazyList.from(1))(_ == _), 1) - // assertLazyAllSkipping(_.corresponds(Iterator.from(0).take(1))(_ == _), 2) + assertLazyAllSkipping(_.corresponds(Iterator.from(0).take(1))(_ == _), 2) assertLazyAllSkipping(_.corresponds(LazyList.from(0).take(1))(_ == _), 2) // check laziness of corresponding `LazyList` @@ -563,10 +562,7 @@ class LazyListLazinessTest { } { val op = lazyListOp(_.patch(from, Nil, replaced)) val d = DropProfile(dropCount = replaced, repeatedDrops = false) - // genericLazyOp_properlyLazy(op, d) - assertLazyAll(op) - assertRepeatedlyLazy(op, d) - // assertKnownEmptyYieldsKnownEmpty(op) + genericLazyOp_properlyLazy(op, d) } } @@ -591,16 +587,16 @@ class LazyListLazinessTest { genericSameElements_properlyLazy(_ == _) } - // @Test - // def search_properlyLazy(): Unit = { - // assertLazyAllSkipping(_.search(0), 1) - // assertLazyAllSkipping(_.search(1), 2) - // assertLazyAllSkipping(_.search(-1, 4, 7), 7) - // } + @Test + def search_properlyLazy(): Unit = { + assertLazyAllSkipping(_.search(0), 1) + assertLazyAllSkipping(_.search(1), 2) + assertLazyAllSkipping(_.search(-1, 4, 7), 7) + } @Test def segmentLength_properlyLazy(): Unit = { - // assertLazyAllSkipping(_.segmentLength(_ => false), 1) + assertLazyAllSkipping(_.segmentLength(_ => false), 1) assertLazyAllSkipping(_.segmentLength(_ => false, 4), 5) } @@ -633,23 +629,22 @@ class LazyListLazinessTest { @Test def zip_properlyLazy(): Unit = { - // assertLazyAll(_.zip(Nil).force) - assertLazyAllSkipping(_.zip(Nil).force, 1) + assertLazyAll(_.zip(Nil).force) val op = lazyListOp(_.zip(LazyList from 0)) assertLazyAll(op) assertKnownEmptyYields(op)(_.knownSize == 0) genericLazyOp_properlyLazy(op.andThen(_.map({ case (a, b) => a + b }))) } - // @Test - // def lazyZip_properlyLazy(): Unit = { - // assertLazyAll(_.lazyZip(Nil).to(LazyList).force) - // val op = lazyListOp(_.lazyZip(LazyList from 0)) - // assertLazyAll(op) - // assertKnownEmptyYields(op)(_.knownSize == 0) - // - // genericLazyOp_properlyLazy(op.andThen(_.map({ case (a, b) => a + b }))) - // } + @Test + def lazyZip_properlyLazy(): Unit = { + assertLazyAll(_.lazyZip(Nil).to(LazyList).force) + val op = lazyListOp(_.lazyZip(LazyList from 0)) + assertLazyAll(op) + assertKnownEmptyYields(op)(_.knownSize == 0) + + genericLazyOp_properlyLazy(op.andThen(_.map({ case (a, b) => a + b }))) + } @Test def zipWithIndex_properlyLazy(): Unit = { @@ -663,7 +658,7 @@ class LazyListLazinessTest { def zipAll_properlyLazy(): Unit = { val op1 = lazyListOp(_.zipAll(Nil, 0, 0)) assertLazyAll(op1) - // assertKnownEmptyYields(op1)(_.knownSize == 0) + assertKnownEmptyYields(op1)(_.knownSize == 0) val op2 = lazyListOp(_.zipAll(LazyList.tabulate(LazinessChecker.doubleCount)(i => i), 0, 0)) for (op <- op1 :: op2 :: Nil) { @@ -696,25 +691,24 @@ class LazyListLazinessTest { } } - // This test does not work across all Scala versions, or on scala.js - // @Test - // def serialization_properlyLazy(): Unit = { - // def serializeDeserialize(obj: LazyList[Int]): LazyList[Int] = { - // import java.io._ - // val buffer = new ByteArrayOutputStream - // val out = new ObjectOutputStream(buffer) - // out.writeObject(obj) - // val in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray)) - // in.readObject.asInstanceOf[LazyList[Int]] - // } - // - // assertLazyAll(serializeDeserialize) - // val op = lazyListOp { list => - // list.take(4).force - // serializeDeserialize(list) - // } - // assertLazyAllSkipping(op, 4) - // } + @Test + def serialization_properlyLazy(): Unit = { + def serializeDeserialize(obj: LazyList[Int]): LazyList[Int] = { + import java.io._ + val buffer = new ByteArrayOutputStream + val out = new ObjectOutputStream(buffer) + out.writeObject(obj) + val in = new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray)) + in.readObject.asInstanceOf[LazyList[Int]] + } + + assertLazyAll(serializeDeserialize) + val op = lazyListOp { list => + list.take(4).force + serializeDeserialize(list) + } + assertLazyAllSkipping(op, 4) + } private def genericCons_unapply_properlyLazy( unapply: LazyList[Int] => Option[(Int, LazyList[Int])]): Unit = { @@ -755,7 +749,7 @@ class LazyListLazinessTest { if (i >= LazinessChecker.count) None else { init.evaluate(i) - Some(i, i + 1) + Some((i, i + 1)) } } } @@ -819,19 +813,18 @@ class LazyListLazinessTest { } @Test - @Ignore // TODO: enable after upgrading to 2.13.4+ + @Ignore // TODO: enable after upgrading to 3.0.0-M2 (2.13.4 has the fix, but M1 is still at 2.13.3) def cons_properlyLazy(): Unit = { genericCons_properlyLazy(LazyList.cons(_, _)) } @Test def `#::_properlyLazy`(): Unit = { - // genericCons_properlyLazy(_ #:: _) - genericCons_properlyLazy((hd, tl) => tl.#::(hd)) + genericCons_properlyLazy(_ #:: _) } @Test - def `#::: properlyLazy`(): Unit = { + def `#:::_properlyLazy`(): Unit = { val headInitFactory = lazyListFactory { init => def gen(index: Int): LazyList[Int] = { def elem(): LazyList[Int] = LazyList.fill(1) { init.evaluate(index); index } @@ -891,7 +884,7 @@ class LazyListLazinessTest { @Test def concat_companion_properlyLazy(): Unit = { val quarterCount = LazinessChecker.count / 4 - // assertEquals(0, LazyList.concat().knownSize) + assertEquals(0, LazyList.concat().knownSize) val factory = lazyListFactory { init => val lists = (0 until 4) map { i => @@ -910,32 +903,24 @@ class LazyListLazinessTest { @Test def range_properlyLazy(): Unit = { var counter = 0 - class CustomLong(val value: Long) { + case class CustomLong(value: Long) { counter += 1 - - override def equals(obj: Any): Boolean = obj match { - case that: CustomLong => this.value == that.value - case _ => false - } - override def hashCode(): Int = value.## } object CustomLong { import scala.language.implicitConversions - def apply(value: Long): CustomLong = new CustomLong(value) - implicit def long2CustomLong(long: Long): CustomLong = apply(long) + implicit def long2CustomLong(long: Long): CustomLong = CustomLong(long) implicit val customIntegralIsIntegral: Integral[CustomLong] = new Integral[CustomLong] { - private val I = implicitly[Integral[Long]] - - override def quot(x: CustomLong, y: CustomLong) = I.quot(x.value, y.value) - override def rem(x: CustomLong, y: CustomLong) = I.rem(x.value, y.value) - override def plus(x: CustomLong, y: CustomLong) = I.plus(x.value, y.value) - override def minus(x: CustomLong, y: CustomLong) = I.minus(x.value, y.value) - override def times(x: CustomLong, y: CustomLong) = I.times(x.value, y.value) - override def negate(x: CustomLong) = I.negate(x.value) - override def fromInt(x: Int) = I.fromInt(x) - def parseString(str: String): Option[CustomLong] = - scala.util.Try(str.toLong).toOption.map(CustomLong.apply) + private val I = Integral[Long] + + override def quot(x: CustomLong, y: CustomLong) = I.quot(x.value, y.value) + override def rem(x: CustomLong, y: CustomLong) = I.rem(x.value, y.value) + override def plus(x: CustomLong, y: CustomLong) = I.plus(x.value, y.value) + override def minus(x: CustomLong, y: CustomLong) = I.minus(x.value, y.value) + override def times(x: CustomLong, y: CustomLong) = I.times(x.value, y.value) + override def negate(x: CustomLong) = I.negate(x.value) + override def fromInt(x: Int) = I.fromInt(x) + override def parseString(str: String) = I.parseString(str).map(CustomLong.apply) override def toInt(x: CustomLong) = I.toInt(x.value) override def toLong(x: CustomLong) = I.toLong(x.value) override def toFloat(x: CustomLong) = I.toFloat(x.value) @@ -957,7 +942,7 @@ class LazyListLazinessTest { @Test def unapplySeq_properlyLazy(): Unit = { - genericLazyOp_properlyLazy(LazyList.unapplySeq(_).get.toSeq.to(LazyList)) + genericLazyOp_properlyLazy(LazyList.unapplySeq(_).toSeq.to(LazyList)) } @Test @@ -1147,7 +1132,7 @@ private object LazyListLazinessTest { * a lazy list that is known to be empty. */ def assertKnownEmptyYields[A](op: LazyListOp[A])(predicate: A => Boolean): Unit = { - assertTrue("not known to be empty", predicate(op(LazyList.empty))) + assert(predicate(op(LazyList.empty))) } /** Asserts that operation yields a lazy list that is known to be empty