diff --git a/src/main/scala/strawman/collection/Iterable.scala b/src/main/scala/strawman/collection/Iterable.scala index 079adcda33..70fa9e969f 100644 --- a/src/main/scala/strawman/collection/Iterable.scala +++ b/src/main/scala/strawman/collection/Iterable.scala @@ -2,15 +2,160 @@ package strawman.collection import scala.annotation.unchecked.uncheckedVariance import scala.reflect.ClassTag -import scala.{Int, Boolean, Array, Any, Unit, StringContext} +import scala.{Any, AnyVal, Array, Boolean, Int, StringContext, Unit} import java.lang.String -import strawman.collection.mutable.{ArrayBuffer, StringBuilder} +import strawman.collection.mutable.{ArrayBuffer, Iterator, StringBuilder} /** Base trait for generic collections */ -trait Iterable[+A] extends IterableOnce[A] with IterableLike[A, Iterable] { +trait Iterable[+A] extends IterableLike[A, Iterable] { /** The collection itself */ protected def coll: this.type = this + + /** Iterator can be used only once */ + def iterator(): Iterator[A] + + /** Operations based on the underlying `Iterator` */ + final def iterating: Iterating[A] = new Iterating(this) + +} + +/** Low-level operations based on the underlying `Iterator` of an `Iterable` */ +class Iterating[+A](val iterable: Iterable[A]) extends AnyVal { + + def foldLeft[B](z: B)(op: (B, A) => B): B = { + val it = iterable.iterator() + var b = z + while (it.hasNext) { + b = op(b, it.next()) + } + b + } + + def foldRight[B](z: B)(op: (A, B) => B): B = { + val it = iterable.iterator() + def loop(): B = if (it.hasNext) op(it.next(), loop()) else z + loop() + } + + def foreach(f: A => Unit): Unit = { + val it = iterable.iterator() + while (it.hasNext) f(it.next()) + } + + def indexWhere(p: A => Boolean): Int = { + val it = iterable.iterator() + var i = 0 + while (it.hasNext) { + if (p(it.next())) return i + i += 1 + } + -1 + } + + def length: Int = { + val it = iterable.iterator() + var len = 0 + while (it.hasNext) { len += 1; it.next() } + len + } + + def filter(p: A => Boolean): Iterator[A] = { + val it = iterable.iterator() + new Iterator[A] { + private var hd: A = _ + private var hdDefined: Boolean = false + + def hasNext: Boolean = hdDefined || { + do { + if (!it.hasNext) return false + hd = it.next() + } while (!p(hd)) + hdDefined = true + true + } + + def next() = + if (hasNext) { + hdDefined = false + hd + } + else Iterator.empty.next() + } + } + + def map[B](f: A => B): Iterator[B] = { + val it = iterable.iterator() + new Iterator[B] { + def hasNext = it.hasNext + def next() = f(it.next()) + } + } + + def flatMap[B](f: A => Iterable[B]): Iterator[B] = { + val it = iterable.iterator() + new Iterator[B] { + private var myCurrent: Iterator[B] = Iterator.empty + private def current = { + while (!myCurrent.hasNext && it.hasNext) + myCurrent = f(it.next()).iterator() + myCurrent + } + def hasNext = current.hasNext + def next() = current.next() + } + } + + def ++ [B >: A](bs: Iterable[B]): Iterator[B] = { + val it = iterable.iterator() + new Iterator[B] { + private var myCurrent: Iterator[B] = it + private var first = true + private def current = { + if (!myCurrent.hasNext && first) { + myCurrent = bs.iterator() + first = false + } + myCurrent + } + def hasNext = current.hasNext + def next() = current.next() + } + } + + def take(n: Int): Iterator[A] = { + val it = iterable.iterator() + new Iterator[A] { + private var i = 0 + def hasNext = it.hasNext && i < n + def next() = + if (hasNext) { + i += 1 + it.next() + } + else Iterator.empty.next() + } + } + + def drop(n: Int): Iterator[A] = { + val it = iterable.iterator() + var i = 0 + while (i < n && it.hasNext) { + it.next() + i += 1 + } + it + } + + def zip[B](that: Iterable[B]): Iterator[(A, B)] = { + val it = iterable.iterator() + new Iterator[(A, B)] { + val thatIterator = that.iterator() + def hasNext = it.hasNext && thatIterator.hasNext + def next() = (it.next(), thatIterator.next()) + } + } + } /** Base trait for Iterable operations @@ -52,19 +197,19 @@ trait IterableFactory[+C[X] <: Iterable[X]] extends FromIterable[C] { */ trait IterableOps[+A] extends Any { protected def coll: Iterable[A] - private def iterator() = coll.iterator() + private def iterator(): Iterator[A] = coll.iterator() /** Apply `f` to each element for tis side effects */ - def foreach(f: A => Unit): Unit = iterator().foreach(f) + def foreach(f: A => Unit): Unit = coll.iterating.foreach(f) /** Fold left */ - def foldLeft[B](z: B)(op: (B, A) => B): B = iterator().foldLeft(z)(op) + def foldLeft[B](z: B)(op: (B, A) => B): B = coll.iterating.foldLeft(z)(op) /** Fold right */ - def foldRight[B](z: B)(op: (A, B) => B): B = iterator().foldRight(z)(op) + def foldRight[B](z: B)(op: (A, B) => B): B = coll.iterating.foldRight(z)(op) /** The index of the first element in this collection for which `p` holds. */ - def indexWhere(p: A => Boolean): Int = iterator().indexWhere(p) + def indexWhere(p: A => Boolean): Int = coll.iterating.indexWhere(p) /** Is the collection empty? */ def isEmpty: Boolean = !iterator().hasNext @@ -80,7 +225,7 @@ trait IterableOps[+A] extends Any { /** The number of elements in this collection. Does not terminate for * infinite collections. */ - def size: Int = if (knownSize >= 0) knownSize else iterator().length + def size: Int = if (knownSize >= 0) knownSize else coll.iterating.length /** A view representing the elements of this collection. */ def view: View[A] = View.fromIterator(iterator()) @@ -179,12 +324,12 @@ trait IterablePolyTransforms[+A, +C[A]] extends Any { def map[B](f: A => B): C[B] = fromIterable(View.Map(coll, f)) /** Flatmap */ - def flatMap[B](f: A => IterableOnce[B]): C[B] = fromIterable(View.FlatMap(coll, f)) + def flatMap[B](f: A => Iterable[B]): C[B] = fromIterable(View.FlatMap(coll, f)) /** Concatenation */ - def ++[B >: A](xs: IterableOnce[B]): C[B] = fromIterable(View.Concat(coll, xs)) + def ++[B >: A](xs: Iterable[B]): C[B] = fromIterable(View.Concat(coll, xs)) /** Zip. Interesting because it requires to align to source collections. */ - def zip[B](xs: IterableOnce[B]): C[(A @uncheckedVariance, B)] = fromIterable(View.Zip(coll, xs)) + def zip[B](xs: Iterable[B]): C[(A @uncheckedVariance, B)] = fromIterable(View.Zip(coll, xs)) // sound bcs of VarianceNote } diff --git a/src/main/scala/strawman/collection/IterableOnce.scala b/src/main/scala/strawman/collection/IterableOnce.scala deleted file mode 100644 index be034b04d4..0000000000 --- a/src/main/scala/strawman/collection/IterableOnce.scala +++ /dev/null @@ -1,8 +0,0 @@ -package strawman.collection - -import strawman.collection.mutable.Iterator - -trait IterableOnce[+A] { - /** Iterator can be used only once */ - def iterator(): Iterator[A] -} \ No newline at end of file diff --git a/src/main/scala/strawman/collection/Seq.scala b/src/main/scala/strawman/collection/Seq.scala index f28a9849f0..bd47cdfd92 100644 --- a/src/main/scala/strawman/collection/Seq.scala +++ b/src/main/scala/strawman/collection/Seq.scala @@ -28,7 +28,7 @@ trait LinearSeq[+A] extends Seq[A] with LinearSeqLike[A, LinearSeq] { self => } /** `length` is defined in terms of `iterator` */ - def length: Int = iterator().length + def length: Int = iterating.length /** `apply` is defined in terms of `drop`, which is in turn defined in * terms of `tail`. diff --git a/src/main/scala/strawman/collection/View.scala b/src/main/scala/strawman/collection/View.scala index 5b58ca66d0..072e42b753 100644 --- a/src/main/scala/strawman/collection/View.scala +++ b/src/main/scala/strawman/collection/View.scala @@ -36,7 +36,7 @@ object View { /** A view that filters an underlying collection. */ case class Filter[A](underlying: Iterable[A], p: A => Boolean) extends View[A] { - def iterator() = underlying.iterator().filter(p) + def iterator() = underlying.iterating.filter(p) } /** A view that partitions an underlying collection into two views */ @@ -55,12 +55,12 @@ object View { /** A view representing one half of a partition. */ case class Partitioned[A](partition: Partition[A], cond: Boolean) extends View[A] { - def iterator() = partition.underlying.iterator().filter(x => partition.p(x) == cond) + def iterator() = partition.underlying.iterating.filter(x => partition.p(x) == cond) } /** A view that drops leading elements of the underlying collection. */ case class Drop[A](underlying: Iterable[A], n: Int) extends View[A] { - def iterator() = underlying.iterator().drop(n) + def iterator() = underlying.iterating.drop(n) protected val normN = n max 0 override def knownSize = if (underlying.knownSize >= 0) (underlying.knownSize - normN) max 0 else -1 @@ -68,7 +68,7 @@ object View { /** A view that takes leading elements of the underlying collection. */ case class Take[A](underlying: Iterable[A], n: Int) extends View[A] { - def iterator() = underlying.iterator().take(n) + def iterator() = underlying.iterating.take(n) protected val normN = n max 0 override def knownSize = if (underlying.knownSize >= 0) underlying.knownSize min normN else -1 @@ -76,20 +76,20 @@ object View { /** A view that maps elements of the underlying collection. */ case class Map[A, B](underlying: Iterable[A], f: A => B) extends View[B] { - def iterator() = underlying.iterator().map(f) + def iterator() = underlying.iterating.map(f) override def knownSize = underlying.knownSize } /** A view that flatmaps elements of the underlying collection. */ - case class FlatMap[A, B](underlying: Iterable[A], f: A => IterableOnce[B]) extends View[B] { - def iterator() = underlying.iterator().flatMap(f) + case class FlatMap[A, B](underlying: Iterable[A], f: A => Iterable[B]) extends View[B] { + def iterator() = underlying.iterating.flatMap(f) } /** A view that concatenates elements of the underlying collection with the elements * of another collection or iterator. */ - case class Concat[A](underlying: Iterable[A], other: IterableOnce[A]) extends View[A] { - def iterator() = underlying.iterator() ++ other + case class Concat[A](underlying: Iterable[A], other: Iterable[A]) extends View[A] { + def iterator() = underlying.iterating ++ other override def knownSize = other match { case other: Iterable[_] if underlying.knownSize >= 0 && other.knownSize >= 0 => underlying.knownSize + other.knownSize @@ -101,8 +101,8 @@ object View { /** A view that zips elements of the underlying collection with the elements * of another collection or iterator. */ - case class Zip[A, B](underlying: Iterable[A], other: IterableOnce[B]) extends View[(A, B)] { - def iterator() = underlying.iterator().zip(other) + case class Zip[A, B](underlying: Iterable[A], other: Iterable[B]) extends View[(A, B)] { + def iterator() = underlying.iterating.zip(other) override def knownSize = other match { case other: Iterable[_] => underlying.knownSize min other.knownSize case _ => -1 diff --git a/src/main/scala/strawman/collection/immutable/List.scala b/src/main/scala/strawman/collection/immutable/List.scala index 623062dde8..88462ebdc1 100644 --- a/src/main/scala/strawman/collection/immutable/List.scala +++ b/src/main/scala/strawman/collection/immutable/List.scala @@ -3,7 +3,7 @@ package strawman.collection.immutable import scala.annotation.unchecked.uncheckedVariance import scala.Nothing import scala.Predef.??? -import strawman.collection.{Iterable, IterableFactory, IterableOnce, LinearSeq, SeqLike} +import strawman.collection.{Iterable, IterableFactory, LinearSeq, SeqLike} import strawman.collection.mutable.{Buildable, ListBuffer} @@ -26,7 +26,7 @@ sealed trait List[+A] else prefix.head :: prefix.tail ++: this /** When concatenating with another list `xs`, avoid copying `xs` */ - override def ++[B >: A](xs: IterableOnce[B]): List[B] = xs match { + override def ++[B >: A](xs: Iterable[B]): List[B] = xs match { case xs: List[B] => this ++: xs case _ => super.++(xs) } diff --git a/src/main/scala/strawman/collection/javaSupport.scala b/src/main/scala/strawman/collection/javaSupport.scala index 924542f117..ddff879d9b 100644 --- a/src/main/scala/strawman/collection/javaSupport.scala +++ b/src/main/scala/strawman/collection/javaSupport.scala @@ -56,9 +56,9 @@ class StringOps(val s: String) /** Overloaded version of `++` that gives back a string, where the inherited * version gives back a sequence. */ - def ++(xs: IterableOnce[Char]): String = { + def ++(xs: Iterable[Char]): String = { val sb = new StringBuilder() ++= s - for (ch <- xs.iterator()) sb += ch + for (ch <- xs.iterating) sb += ch sb.result } @@ -100,9 +100,9 @@ class ArrayOps[A](val xs: Array[A]) override def className = "Array" def map[B: ClassTag](f: A => B): Array[B] = fromIterable(View.Map(coll, f)) - def flatMap[B: ClassTag](f: A => IterableOnce[B]): Array[B] = fromIterable(View.FlatMap(coll, f)) - def ++[B >: A : ClassTag](xs: IterableOnce[B]): Array[B] = fromIterable(View.Concat(coll, xs)) - def zip[B: ClassTag](xs: IterableOnce[B]): Array[(A, B)] = fromIterable(View.Zip(coll, xs)) + def flatMap[B: ClassTag](f: A => Iterable[B]): Array[B] = fromIterable(View.FlatMap(coll, f)) + def ++[B >: A : ClassTag](xs: Iterable[B]): Array[B] = fromIterable(View.Concat(coll, xs)) + def zip[B: ClassTag](xs: Iterable[B]): Array[(A, B)] = fromIterable(View.Zip(coll, xs)) } case class ArrayView[A](xs: Array[A]) extends IndexedView[A] { diff --git a/src/main/scala/strawman/collection/mutable/ArrayBuffer.scala b/src/main/scala/strawman/collection/mutable/ArrayBuffer.scala index 740e71f4b8..64c48797d8 100644 --- a/src/main/scala/strawman/collection/mutable/ArrayBuffer.scala +++ b/src/main/scala/strawman/collection/mutable/ArrayBuffer.scala @@ -2,7 +2,7 @@ package strawman.collection.mutable import scala.{Array, Int, Boolean, Unit, AnyRef} import scala.Predef.intWrapper -import strawman.collection.{IndexedView, Iterable, IterableFactory, IterableOnce, Seq, SeqLike} +import strawman.collection.{IndexedView, Iterable, IterableFactory, Seq, SeqLike} /** Concrete collection type: ArrayBuffer */ class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int) @@ -55,7 +55,7 @@ class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int) def trimStart(n: Int): Unit = start += (n max 0) /** Overridden to use array copying for efficiency where possible. */ - override def ++[B >: A](xs: IterableOnce[B]): ArrayBuffer[B] = xs match { + override def ++[B >: A](xs: Iterable[B]): ArrayBuffer[B] = xs match { case xs: ArrayBuffer[B] => val elems = new Array[AnyRef](length + xs.length) Array.copy(this.elems, this.start, elems, 0, this.length) diff --git a/src/main/scala/strawman/collection/mutable/Builder.scala b/src/main/scala/strawman/collection/mutable/Builder.scala index cbd19aa56e..8e6423bd54 100644 --- a/src/main/scala/strawman/collection/mutable/Builder.scala +++ b/src/main/scala/strawman/collection/mutable/Builder.scala @@ -2,7 +2,7 @@ package strawman.collection.mutable import scala.{Boolean, Any, Char} import java.lang.String -import strawman.collection.{IterableMonoTransforms, IterableOnce} +import strawman.collection.{Iterable, IterableMonoTransforms} /** Base trait for collection builders */ trait Builder[-A, +To] { self => @@ -14,15 +14,15 @@ trait Builder[-A, +To] { self => def result: To /** Bulk append. Can be overridden if specialized implementations are available. */ - def ++=(xs: IterableOnce[A]): this.type = { - xs.iterator().foreach(+=) + def ++=(xs: Iterable[A]): this.type = { + xs.iterating.foreach(+=) this } /** A builder resulting from this builder my mapping the result using `f`. */ def mapResult[NewTo](f: To => NewTo) = new Builder[A, NewTo] { def +=(x: A): this.type = { self += x; this } - override def ++=(xs: IterableOnce[A]): this.type = { self ++= xs; this } + override def ++=(xs: Iterable[A]): this.type = { self ++= xs; this } def result: NewTo = f(self.result) } } @@ -39,7 +39,7 @@ trait Buildable[+A, +Repr] extends Any with IterableMonoTransforms[A, Repr] { /** Optimized, push-based version of `partition`. */ override def partition(p: A => Boolean): (Repr, Repr) = { val l, r = newBuilder - coll.iterator().foreach(x => (if (p(x)) l else r) += x) + coll.iterating.foreach(x => (if (p(x)) l else r) += x) (l.result, r.result) } diff --git a/src/main/scala/strawman/collection/mutable/Iterator.scala b/src/main/scala/strawman/collection/mutable/Iterator.scala index 51ab5c7cba..0552b258b3 100644 --- a/src/main/scala/strawman/collection/mutable/Iterator.scala +++ b/src/main/scala/strawman/collection/mutable/Iterator.scala @@ -1,102 +1,12 @@ package strawman.collection.mutable import scala.{Boolean, Int, Unit, Nothing, NoSuchElementException} -import strawman.collection.{IndexedView, IterableOnce} +import strawman.collection.IndexedView /** A core Iterator class */ trait Iterator[+A] { self => def hasNext: Boolean def next(): A - def foldLeft[B](z: B)(op: (B, A) => B): B = - if (hasNext) foldLeft(op(z, next()))(op) else z - def foldRight[B](z: B)(op: (A, B) => B): B = - if (hasNext) op(next(), foldRight(z)(op)) else z - def foreach(f: A => Unit): Unit = - while (hasNext) f(next()) - def indexWhere(p: A => Boolean): Int = { - var i = 0 - while (hasNext) { - if (p(next())) return i - i += 1 - } - -1 - } - def length = { - var len = 0 - while (hasNext) { len += 1; next() } - len - } - def filter(p: A => Boolean): Iterator[A] = new Iterator[A] { - private var hd: A = _ - private var hdDefined: Boolean = false - - def hasNext: Boolean = hdDefined || { - do { - if (!self.hasNext) return false - hd = self.next() - } while (!p(hd)) - hdDefined = true - true - } - - def next() = - if (hasNext) { - hdDefined = false - hd - } - else Iterator.empty.next() - } - def map[B](f: A => B): Iterator[B] = new Iterator[B] { - def hasNext = self.hasNext - def next() = f(self.next()) - } - - def flatMap[B](f: A => IterableOnce[B]): Iterator[B] = new Iterator[B] { - private var myCurrent: Iterator[B] = Iterator.empty - private def current = { - while (!myCurrent.hasNext && self.hasNext) - myCurrent = f(self.next()).iterator() - myCurrent - } - def hasNext = current.hasNext - def next() = current.next() - } - def ++[B >: A](xs: IterableOnce[B]): Iterator[B] = new Iterator[B] { - private var myCurrent: Iterator[B] = self - private var first = true - private def current = { - if (!myCurrent.hasNext && first) { - myCurrent = xs.iterator() - first = false - } - myCurrent - } - def hasNext = current.hasNext - def next() = current.next() - } - def take(n: Int): Iterator[A] = new Iterator[A] { - private var i = 0 - def hasNext = self.hasNext && i < n - def next() = - if (hasNext) { - i += 1 - self.next() - } - else Iterator.empty.next() - } - def drop(n: Int): Iterator[A] = { - var i = 0 - while (i < n && hasNext) { - next() - i += 1 - } - this - } - def zip[B](that: IterableOnce[B]): Iterator[(A, B)] = new Iterator[(A, B)] { - val thatIterator = that.iterator() - def hasNext = self.hasNext && thatIterator.hasNext - def next() = (self.next(), thatIterator.next()) - } } object Iterator { diff --git a/src/main/scala/strawman/collection/package.scala b/src/main/scala/strawman/collection/package.scala index ad5664fc5e..ce23b06088 100644 --- a/src/main/scala/strawman/collection/package.scala +++ b/src/main/scala/strawman/collection/package.scala @@ -22,7 +22,7 @@ import strawman.collection.mutable.{ArrayBuffer, Buildable} * * 1. Collection base types: * - * IterableOnce, Iterable, Seq, LinearSeq, View, IndexedView + * Iterable, Seq, LinearSeq, View, IndexedView * * 2. Collection creator base types: *