package strawman.collections import Predef.{augmentString => _, wrapString => _, _} import scala.reflect.ClassTag import annotation.unchecked.uncheckedVariance import annotation.tailrec class LowPriority { import CollectionStrawMan6._ /** Convert array to iterable via view. Lower priority than ArrayOps */ implicit def arrayToView[T](xs: Array[T]): ArrayView[T] = new ArrayView[T](xs) /** Convert string to iterable via view. Lower priority than StringOps */ implicit def stringToView(s: String): StringView = new StringView(s) } /** A strawman architecture for new collections. It contains some * example collection classes and methods with the intent to expose * some key issues. It would be good to compare this to odether * implementations of the same functionality, to get an idea of the * strengths and weaknesses of different collection architectures. * * For a test file, see tests/run/CollectionTests.scala. * * Strawman6 is like strawman5, and adds lazy lists (i.e. lazie streams), arrays * and some utilitity methods (take, tail, mkString, toArray). Also, systematically * uses builders for all strict collections. * * Types covered in this strawman: * * 1. Collection base types: * * IterableOnce, Iterable, Seq, LinearSeq, View, IndexedView * * 2. Collection creator base types: * * FromIterable, IterableFactory, Buildable, Builder * * 3. Types that bundle operations: * * IterableOps, IterableMonoTransforms, IterablePolyTransforms, IterableLike * SeqMonoTransforms, SeqLike * * 4. Concrete collection types: * * List, LazyList, ListBuffer, ArrayBuffer, ArrayBufferView, StringView, ArrayView * * 5. Decorators for existing types * * StringOps, ArrayOps * * 6. Related non collection types: * * Iterator, StringBuilder * * Operations covered in this strawman: * * 1. Abstract operations, or expected to be overridden: * * For iterables: * * iterator, fromIterable, fromIterableWithSameElemType, knownLength, className * * For sequences: * * apply, length * * For buildables: * * newBuilder * * For builders: * * +=, result * * 2. Utility methods, might be overridden for performance: * * Operations returning not necessarily a collection: * * foreach, foldLeft, foldRight, indexWhere, isEmpty, head, size, mkString * * Operations returning a collection of a fixed type constructor: * * view, to, toArray, copyToArray * * Type-preserving generic transforms: * * filter, partition, take, drop, tail, reverse * * Generic transforms returning collections of different element types: * * map, flatMap, ++, zip */ object CollectionStrawMan6 extends LowPriority { /* ------------ Base Traits -------------------------------- */ /** Iterator can be used only once */ trait IterableOnce[+A] { def iterator: Iterator[A] } /** Base trait for instances that can construct a collection from an iterable */ trait FromIterable[+C[X] <: Iterable[X]] { def fromIterable[B](it: Iterable[B]): C[B] } /** Base trait for companion objects of collections */ trait IterableFactory[+C[X] <: Iterable[X]] extends FromIterable[C] { def empty[X]: C[X] = fromIterable(View.Empty) def apply[A](xs: A*): C[A] = fromIterable(View.Elems(xs: _*)) } /** Base trait for generic collections */ trait Iterable[+A] extends IterableOnce[A] with IterableLike[A, Iterable] { /** The collection itself */ protected def coll: this.type = this } /** A trait representing indexable collections with finite length */ trait ArrayLike[+A] extends Any { def length: Int def apply(i: Int): A } /** Base trait for sequence collections */ trait Seq[+A] extends Iterable[A] with SeqLike[A, Seq] with ArrayLike[A] /** Base trait for linearly accessed sequences that have efficient `head` and * `tail` operations. * Known subclasses: List, LazyList */ trait LinearSeq[+A] extends Seq[A] with LinearSeqLike[A, LinearSeq] { self => /** To be overridden in implementations: */ def isEmpty: Boolean def head: A def tail: LinearSeq[A] /** `iterator` is overridden in terms of `head` and `tail` */ def iterator = new Iterator[A] { private[this] var current: Seq[A] = self def hasNext = !current.isEmpty def next = { val r = current.head; current = current.tail; r } } /** `length is defined in terms of `iterator` */ def length: Int = iterator.length /** `apply` is defined in terms of `drop`, which is in turn defined in * terms of `tail`. */ override def apply(n: Int): A = { if (n < 0) throw new IndexOutOfBoundsException(n.toString) val skipped = drop(n) if (skipped.isEmpty) throw new IndexOutOfBoundsException(n.toString) skipped.head } } type IndexedSeq[+A] = Seq[A] { def view: IndexedView[A] } /** Base trait for strict collections that can be built using a builder. * @param A the element type of the collection * @param Repr the type of the underlying collection */ trait Buildable[+A, +Repr] extends Any with IterableMonoTransforms[A, Repr] { /** Creates a new builder. */ protected[this] def newBuilder: Builder[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) (l.result, r.result) } // one might also override other transforms here to avoid generating // iterators if it helps efficiency. } /** Base trait for collection builders */ trait Builder[-A, +To] { self => /** Append an element */ def +=(x: A): this.type /** Result collection consisting of all elements appended so far. */ def result: To /** Bulk append. Can be overridden if specialized implementations are available. */ def ++=(xs: IterableOnce[A]): this.type = { xs.iterator.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 } def result: NewTo = f(self.result) } } /* ------------ Operations ----------------------------------- */ /** Base trait for Iterable operations * * VarianceNote * ============ * * We require that for all child classes of Iterable the variance of * the child class and the variance of the `C` parameter passed to `IterableLike` * are the same. We cannot express this since we lack variance polymorphism. That's * why we have to resort at some places to write `C[A @uncheckedVariance]`. * */ trait IterableLike[+A, +C[X] <: Iterable[X]] extends FromIterable[C] with IterableOps[A] with IterableMonoTransforms[A, C[A @uncheckedVariance]] // sound bcs of VarianceNote with IterablePolyTransforms[A, C] { /** Create a collection of type `C[A]` from the elements of `coll`, which has * the same element type as this collection. Overridden in StringOps and ArrayOps. */ protected[this] def fromIterableWithSameElemType(coll: Iterable[A]): C[A] = fromIterable(coll) } /** Base trait for Seq operations */ trait SeqLike[+A, +C[X] <: Seq[X]] extends IterableLike[A, C] with SeqMonoTransforms[A, C[A @uncheckedVariance]] // sound bcs of VarianceNote /** Base trait for linear Seq operations */ trait LinearSeqLike[+A, +C[X] <: LinearSeq[X]] extends SeqLike[A, C] { /** Optimized version of `drop` that avoids copying * Note: `drop` is defined here, rather than in a trait like `LinearSeqMonoTransforms`, * because the `...MonoTransforms` traits make no assumption about the type of `Repr` * whereas we need to assume here that `Repr` is the same as the underlying * collection type. */ override def drop(n: Int): C[A @uncheckedVariance] = { // sound bcs of VarianceNote @tailrec def loop(n: Int, s: Iterable[A]): C[A] = if (n <= 0) s.asInstanceOf[C[A]] // implicit contract to guarantee success of asInstanceOf: // (1) coll is of type C[A] // (2) The tail of a LinearSeq is of the same type as the type of the sequence itself // it's surprisingly tricky/ugly to turn this into actual types, so we // leave this contract implicit. else loop(n - 1, s.tail) loop(n, coll) } } /** Operations over iterables. No operation defined here is generic in the * type of the underlying collection. */ trait IterableOps[+A] extends Any { protected def coll: Iterable[A] private def iterator = coll.iterator /** Apply `f` to each element for tis side effects */ def foreach(f: A => Unit): Unit = iterator.foreach(f) /** Fold left */ def foldLeft[B](z: B)(op: (B, A) => B): B = iterator.foldLeft(z)(op) /** Fold right */ def foldRight[B](z: B)(op: (A, B) => B): B = iterator.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) /** Is the collection empty? */ def isEmpty: Boolean = !iterator.hasNext /** The first element of the collection. */ def head: A = iterator.next() /** The number of elements in this collection, if it can be cheaply computed, * -1 otherwise. Cheaply usually means: Not requiring a collection traversal. */ def knownSize: Int = -1 /** The number of elements in this collection. Does not terminate for * infinite collections. */ def size: Int = if (knownSize >= 0) knownSize else iterator.length /** A view representing the elements of this collection. */ def view: View[A] = View.fromIterator(iterator) /** Given a collection factory `fi` for collections of type constructor `C`, * convert this collection to one of type `C[A]`. Example uses: * * xs.to(List) * xs.to(ArrayBuffer) */ def to[C[X] <: Iterable[X]](fi: FromIterable[C]): C[A @uncheckedVariance] = // variance seems sound because `to` could just as well have been added // as a decorator. We should investigate this further to be sure. fi.fromIterable(coll) /** Convert collection to array. */ def toArray[B >: A: ClassTag]: Array[B] = if (knownSize >= 0) copyToArray(new Array[B](knownSize), 0) else ArrayBuffer.fromIterable(coll).toArray[B] /** Copy all elements of this collection to array `xs`, starting at `start`. */ def copyToArray[B >: A](xs: Array[B], start: Int = 0): xs.type = { var i = start val it = iterator while (it.hasNext) { xs(i) = it.next() i += 1 } xs } /** The class name of this collection. To be used for converting to string. * Collections generally print like this: * * (elem_1, ..., elem_n) */ def className = getClass.getName /** A string showing all elements of this collection, separated by string `sep`. */ def mkString(sep: String): String = { var first: Boolean = true val b = new StringBuilder() foreach { elem => if (!first) b ++= sep first = false b ++= String.valueOf(elem) } b.result } override def toString = s"$className(${mkString(", ")})" } /** Type-preserving transforms over iterables. * Operations defined here return in their result iterables of the same type * as the one they are invoked on. */ trait IterableMonoTransforms[+A, +Repr] extends Any { protected def coll: Iterable[A] protected[this] def fromIterableWithSameElemType(coll: Iterable[A]): Repr /** All elements satisfying predicate `p` */ def filter(p: A => Boolean): Repr = fromIterableWithSameElemType(View.Filter(coll, p)) /** A pair of, first, all elements that satisfy prediacte `p` and, second, * all elements that do not. Interesting because it splits a collection in two. * * The default implementation provided here needs to traverse the collection twice. * Strict collections have an overridden version of `partition` in `Buildable`, * which requires only a single traversal. */ def partition(p: A => Boolean): (Repr, Repr) = { val pn = View.Partition(coll, p) (fromIterableWithSameElemType(pn.left), fromIterableWithSameElemType(pn.right)) } /** A collection containing the first `n` elements of this collection. */ def take(n: Int): Repr = fromIterableWithSameElemType(View.Take(coll, n)) /** The rest of the collection without its `n` first elements. For * linear, immutable collections this should avoid making a copy. */ def drop(n: Int): Repr = fromIterableWithSameElemType(View.Drop(coll, n)) /** The rest of the collection without its first element. */ def tail: Repr = drop(1) } /** Transforms over iterables that can return collections of different element types. */ trait IterablePolyTransforms[+A, +C[A]] extends Any { protected def coll: Iterable[A] def fromIterable[B](coll: Iterable[B]): C[B] /** Map */ 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)) /** Concatenation */ def ++[B >: A](xs: IterableOnce[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)) // sound bcs of VarianceNote } /** Type-preserving transforms over sequences. */ trait SeqMonoTransforms[+A, +Repr] extends Any with IterableMonoTransforms[A, Repr] { def reverse: Repr = coll.view match { case v: IndexedView[A] => fromIterableWithSameElemType(v.reverse) case _ => var xs: List[A] = Nil var it = coll.iterator while (it.hasNext) xs = it.next() :: xs fromIterableWithSameElemType(xs) } } /* --------- Concrete collection types ------------------------------- */ /** Concrete collection type: List */ sealed trait List[+A] extends LinearSeq[A] with SeqLike[A, List] with Buildable[A, List[A]] { def fromIterable[B](c: Iterable[B]): List[B] = List.fromIterable(c) protected[this] def newBuilder = new ListBuffer[A].mapResult(_.toList) /** Prepend element */ def :: [B >: A](elem: B): List[B] = new ::(elem, this) /** Prepend operation that avoids copying this list */ def ++:[B >: A](prefix: List[B]): List[B] = if (prefix.isEmpty) this 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 { case xs: List[B] => this ++: xs case _ => super.++(xs) } override def className = "List" } case class :: [+A](x: A, private[collections] var next: List[A @uncheckedVariance]) // sound because `next` is used only locally extends List[A] { override def isEmpty = false override def head = x override def tail = next } case object Nil extends List[Nothing] { override def isEmpty = true override def head = ??? override def tail = ??? } object List extends IterableFactory[List] { def fromIterable[B](coll: Iterable[B]): List[B] = coll match { case coll: List[B] => coll case _ => ListBuffer.fromIterable(coll).toList } } /** Concrete collection type: ListBuffer */ class ListBuffer[A] extends Seq[A] with SeqLike[A, ListBuffer] with Buildable[A, ListBuffer[A]] with Builder[A, ListBuffer[A]] { private var first, last: List[A] = Nil private var aliased = false private var len = 0 def iterator = first.iterator def fromIterable[B](coll: Iterable[B]) = ListBuffer.fromIterable(coll) def apply(i: Int) = first.apply(i) def length = len override def knownSize = len protected[this] def newBuilder = new ListBuffer[A] private def copyElems(): Unit = { val buf = ListBuffer.fromIterable(result) first = buf.first last = buf.last aliased = false } /** Convert to list; avoids copying where possible. */ def toList = { aliased = true first } def +=(elem: A) = { if (aliased) copyElems() val last1 = elem :: Nil last match { case last: ::[A] => last.next = last1 case _ => first = last1 } last = last1 len += 1 this } def result = this override def className = "ListBuffer" } object ListBuffer extends IterableFactory[ListBuffer] { def fromIterable[B](coll: Iterable[B]): ListBuffer[B] = new ListBuffer[B] ++= coll } /** Concrete collection type: ArrayBuffer */ class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int) extends Seq[A] with SeqLike[A, ArrayBuffer] with Buildable[A, ArrayBuffer[A]] with Builder[A, ArrayBuffer[A]] { def this() = this(new Array[AnyRef](16), 0) private var elems: Array[AnyRef] = initElems private var start = 0 private var end = initLength def apply(n: Int) = elems(start + n).asInstanceOf[A] def length = end - start override def knownSize = length override def view = new ArrayBufferView(elems, start, end) def iterator = view.iterator def fromIterable[B](it: Iterable[B]): ArrayBuffer[B] = ArrayBuffer.fromIterable(it) protected[this] def newBuilder = new ArrayBuffer[A] def +=(elem: A): this.type = { if (end == elems.length) { if (start > 0) { Array.copy(elems, start, elems, 0, length) end -= start start = 0 } else { val newelems = new Array[AnyRef](end * 2) Array.copy(elems, 0, newelems, 0, end) elems = newelems } } elems(end) = elem.asInstanceOf[AnyRef] end += 1 this } def result = this /** New operation: destructively drop elements at start of buffer. */ 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 { case xs: ArrayBuffer[B] => val elems = new Array[AnyRef](length + xs.length) Array.copy(this.elems, this.start, elems, 0, this.length) Array.copy(xs.elems, xs.start, elems, this.length, xs.length) new ArrayBuffer(elems, elems.length) case _ => super.++(xs) } override def take(n: Int) = { val elems = new Array[AnyRef](n min length) Array.copy(this.elems, this.start, elems, 0, elems.length) new ArrayBuffer(elems, elems.length) } override def className = "ArrayBuffer" } object ArrayBuffer extends IterableFactory[ArrayBuffer] { /** Avoid reallocation of buffer if length is known. */ def fromIterable[B](coll: Iterable[B]): ArrayBuffer[B] = if (coll.knownSize >= 0) { val elems = new Array[AnyRef](coll.knownSize) val it = coll.iterator for (i <- 0 until elems.length) elems(i) = it.next().asInstanceOf[AnyRef] new ArrayBuffer[B](elems, elems.length) } else new ArrayBuffer[B] ++= coll } class ArrayBufferView[A](val elems: Array[AnyRef], val start: Int, val end: Int) extends IndexedView[A] { def length = end - start def apply(n: Int) = elems(start + n).asInstanceOf[A] override def className = "ArrayBufferView" } class LazyList[+A](expr: => LazyList.Evaluated[A]) extends LinearSeq[A] with SeqLike[A, LazyList] { private[this] var evaluated = false private[this] var result: LazyList.Evaluated[A] = _ def force: LazyList.Evaluated[A] = { if (!evaluated) { result = expr evaluated = true } result } override def isEmpty = force.isEmpty override def head = force.get._1 override def tail = force.get._2 def #:: [B >: A](elem: => B): LazyList[B] = new LazyList(Some((elem, this))) def fromIterable[B](c: Iterable[B]): LazyList[B] = LazyList.fromIterable(c) override def className = "LazyList" override def toString = if (evaluated) result match { case None => "Empty" case Some((hd, tl)) => s"$hd #:: $tl" } else "LazyList(?)" } object LazyList extends IterableFactory[LazyList] { type Evaluated[+A] = Option[(A, LazyList[A])] object Empty extends LazyList[Nothing](None) object #:: { def unapply[A](s: LazyList[A]): Evaluated[A] = s.force } def fromIterable[B](coll: Iterable[B]): LazyList[B] = coll match { case coll: LazyList[B] => coll case _ => fromIterator(coll.iterator) } def fromIterator[B](it: Iterator[B]): LazyList[B] = new LazyList(if (it.hasNext) Some(it.next(), fromIterator(it)) else None) } // ------------------ Decorators to add collection ops to existing types ----------------------- /** Decorator to add collection operations to strings. */ implicit class StringOps(val s: String) extends AnyVal with IterableOps[Char] with SeqMonoTransforms[Char, String] with IterablePolyTransforms[Char, List] with Buildable[Char, String] with ArrayLike[Char] { protected def coll = new StringView(s) def iterator = coll.iterator protected def fromIterableWithSameElemType(coll: Iterable[Char]): String = { val sb = new StringBuilder for (ch <- coll) sb += ch sb.result } def fromIterable[B](coll: Iterable[B]): List[B] = List.fromIterable(coll) protected[this] def newBuilder = new StringBuilder def length = s.length def apply(i: Int) = s.charAt(i) override def knownSize = s.length override def className = "String" /** Overloaded version of `map` that gives back a string, where the inherited * version gives back a sequence. */ def map(f: Char => Char): String = { val sb = new StringBuilder for (ch <- s) sb += f(ch) sb.result } /** Overloaded version of `flatMap` that gives back a string, where the inherited * version gives back a sequence. */ def flatMap(f: Char => String): String = { val sb = new StringBuilder for (ch <- s) sb ++= f(ch) sb.result } /** Overloaded version of `++` that gives back a string, where the inherited * version gives back a sequence. */ def ++(xs: IterableOnce[Char]): String = { val sb = new StringBuilder() ++= s for (ch <- xs.iterator) sb += ch sb.result } /** Another overloaded version of `++`. */ def ++(xs: String): String = s + xs } class StringBuilder extends Builder[Char, String] { private val sb = new java.lang.StringBuilder def += (x: Char) = { sb.append(x); this } /** Overloaded version of `++=` that takes a string */ def ++= (s: String) = { sb.append(s); this } def result = sb.toString override def toString = result } case class StringView(s: String) extends IndexedView[Char] { def length = s.length def apply(n: Int) = s.charAt(n) override def className = "StringView" } /** Decorator to add collection operations to arrays. */ implicit class ArrayOps[A](val xs: Array[A]) extends AnyVal with IterableOps[A] with SeqMonoTransforms[A, Array[A]] with Buildable[A, Array[A]] with ArrayLike[A] { protected def coll = new ArrayView(xs) def iterator = coll.iterator def length = xs.length def apply(i: Int) = xs.apply(i) override def view = new ArrayView(xs) def elemTag: ClassTag[A] = ClassTag(xs.getClass.getComponentType) protected def fromIterableWithSameElemType(coll: Iterable[A]): Array[A] = coll.toArray[A](elemTag) def fromIterable[B: ClassTag](coll: Iterable[B]): Array[B] = coll.toArray[B] protected[this] def newBuilder = new ArrayBuffer[A].mapResult(_.toArray(elemTag)) override def knownSize = xs.length 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)) } case class ArrayView[A](xs: Array[A]) extends IndexedView[A] { def length = xs.length def apply(n: Int) = xs(n) override def className = "ArrayView" } /* ---------- Views -------------------------------------------------------*/ /** Concrete collection type: View */ trait View[+A] extends Iterable[A] with IterableLike[A, View] { override def view = this /** Avoid copying if source collection is already a view. */ override def fromIterable[B](c: Iterable[B]): View[B] = c match { case c: View[B] => c case _ => View.fromIterator(c.iterator) } override def className = "View" } /** This object reifies operations on views as case classes */ object View { def fromIterator[A](it: => Iterator[A]): View[A] = new View[A] { def iterator = it } /** The empty view */ case object Empty extends View[Nothing] { def iterator = Iterator.empty override def knownSize = 0 } /** A view with given elements */ case class Elems[A](xs: A*) extends View[A] { def iterator = Iterator(xs: _*) override def knownSize = xs.length // should be: xs.knownSize, but A*'s are not sequences in this strawman. } /** A view that filters an underlying collection. */ case class Filter[A](val underlying: Iterable[A], p: A => Boolean) extends View[A] { def iterator = underlying.iterator.filter(p) } /** A view that partitions an underlying collection into two views */ case class Partition[A](val underlying: Iterable[A], p: A => Boolean) { /** The view consisting of all elements of the underlying collection * that satisfy `p`. */ val left = Partitioned(this, true) /** The view consisting of all elements of the underlying collection * that do not satisfy `p`. */ val right = Partitioned(this, false) } /** 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) } /** 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) protected val normN = n max 0 override def knownSize = if (underlying.knownSize >= 0) (underlying.knownSize - normN) max 0 else -1 } /** 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) protected val normN = n max 0 override def knownSize = if (underlying.knownSize >= 0) underlying.knownSize min normN else -1 } /** 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) 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) } /** 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 override def knownSize = other match { case other: Iterable[_] if underlying.knownSize >= 0 && other.knownSize >= 0 => underlying.knownSize + other.knownSize case _ => -1 } } /** 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) override def knownSize = other match { case other: Iterable[_] if underlying.knownSize >= 0 && other.knownSize >= 0 => underlying.knownSize min other.knownSize case _ => -1 } } } /** View defined in terms of indexing a range */ trait IndexedView[+A] extends View[A] with ArrayLike[A] { self => def iterator: Iterator[A] = new Iterator[A] { private var current = 0 def hasNext = current < self.length def next: A = { val r = apply(current) current += 1 r } } override def take(n: Int): IndexedView[A] = new IndexedView.Take(this, n) override def drop(n: Int): IndexedView[A] = new IndexedView.Drop(this, n) override def map[B](f: A => B): IndexedView[B] = new IndexedView.Map(this, f) def reverse: IndexedView[A] = new IndexedView.Reverse(this) } object IndexedView { class Take[A](underlying: IndexedView[A], n: Int) extends View.Take(underlying, n) with IndexedView[A] { override def iterator = super.iterator // needed to avoid "conflicting overrides" error def length = underlying.length min normN def apply(i: Int) = underlying.apply(i) } class Drop[A](underlying: IndexedView[A], n: Int) extends View.Take(underlying, n) with IndexedView[A] { override def iterator = super.iterator def length = (underlying.length - normN) max 0 def apply(i: Int) = underlying.apply(i + normN) } class Map[A, B](underlying: IndexedView[A], f: A => B) extends View.Map(underlying, f) with IndexedView[B] { override def iterator = super.iterator def length = underlying.length def apply(n: Int) = f(underlying.apply(n)) } case class Reverse[A](underlying: IndexedView[A]) extends IndexedView[A] { def length = underlying.length def apply(i: Int) = underlying.apply(length - 1 - i) } } /* ---------- Iterators ---------------------------------------------------*/ /** A core Iterator class */ trait Iterator[+A] extends IterableOnce[A] { self => def hasNext: Boolean def next(): A def iterator = this 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 { val empty: Iterator[Nothing] = new Iterator[Nothing] { def hasNext = false def next = throw new NoSuchElementException("next on empty iterator") } def apply[A](xs: A*): Iterator[A] = new IndexedView[A] { val length = xs.length def apply(n: Int) = xs(n) }.iterator } }