/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ // $Id$ package scala import Predef._ import collection.mutable.{Buffer,ArrayBuffer} /** Various utilities for instances of Iterable. * * @author Matthias Zenger * @version 1.1, 04/02/2004 */ object Iterable { /* implicit def view[A <% Ordered[A]](x: Iterable[A]): Ordered[Iterable[A]] = new Ordered[Iterable[A]] { def compare[B >: Iterable[A] <% Ordered[B]](that: B): Int = that match { case y: Iterable[A] => val xs = x.elements val ys = y.elements var res = 0 while (xs.hasNext && ys.hasNext && (res == 0)) { res = xs.next compare ys.next } if (xs.hasNext) 1 else if (ys.hasNext) -1 else res case _ => -(that compare x) } } */ /** The minimum element of a non-empty sequence of ordered elements */ def min[A <% Ordered[A]](seq: Iterable[A]): A = { val xs = seq.elements if (!xs.hasNext) throw new IllegalArgumentException("min()") var min = xs.next while (xs.hasNext) { val x = xs.next if (x < min) min = x } min } /** The maximum element of a non-empty sequence of ordered elements */ def max[A <% Ordered[A]](seq: Iterable[A]): A = { val xs = seq.elements if (!xs.hasNext) throw new IllegalArgumentException("max()") var max = xs.next while (xs.hasNext) { val x = xs.next if (max < x) max = x } max } /** The empty iterable object */ val empty = new Iterable[Nothing] { def elements = Iterator.empty } /** A non-strict projection of an iterable. * @author Sean McDirmid */ trait Projection[+A] extends Iterable[A] { override def projection = this /** convert to a copied strict collection */ def force : Iterable[A] = toList /** non-strict */ override def filter(p : A => Boolean) : Projection[A] = new Projection[A] { def elements = Projection.this.elements.filter(p) } /** non-strict */ override def map[B](f: A => B) : Projection[B] = new Projection[B] { def elements = Projection.this.elements.map(f) } /** non-strict */ override def flatMap[B](f: A => Iterable[B]) : Projection[B] = new Projection[B] { def elements = Projection.this.elements.flatMap(a => f(a).elements) } /** non-strict */ override def takeWhile(p: A => Boolean): Projection[A] = new Projection[A] { def elements = Projection.this.elements.takeWhile(p) } /** The projection resulting from the concatenation of this projection with the rest projection. * @param rest The projection that gets appended to this projection */ def append[B >: A](rest : => Iterable[B]): Projection[B] = new Projection[B] { def elements = Projection.this.elements ++ rest.elements } } } /** Collection classes mixing in this class provide a method * elements which returns an iterator over all the * elements contained in the collection. * * @note If a collection has a known size, it should also sub-type Collection. * Only potentially unbounded collections should directly sub-class Iterable. * @author Matthias Zenger * @version 1.1, 04/02/2004 */ trait Iterable[+A] { /** Creates a new iterator over all elements contained in this * object. * * @return the new iterator */ def elements: Iterator[A] /** Appends two iterable objects. * * @return the new iterable object * @deprecated use ++ instead * @note Will not terminate for infinite-sized collections. */ @deprecated def concat[B >: A](that: Iterable[B]): Collection[B] = this ++ that /** Appends two iterable objects. * * @return the new iterable object * @note Will not terminate for infinite-sized collections. */ def ++ [B >: A](that: Iterable[B]): Collection[B] = { val buf = new ArrayBuffer[B] this copyToBuffer buf that copyToBuffer buf buf } /** Returns the iterable resulting from applying the given function * f to each element of this iterable. * * @note Will not terminate for infinite-sized collections. * @param f function to apply to each element. * @return f(a0), ..., f(an) * if this iterable is a0, ..., an. */ def map[B](f: A => B): Iterable[B] = { val buf = new ArrayBuffer[B] val elems = elements while (elems.hasNext) buf += f(elems.next) buf } /** Applies the given function f to each element of * this iterable, then concatenates the results. * * @note Will not terminate for infinite-sized collections. * @param f the function to apply on each element. * @return f(a0) ::: ... ::: f(an) if * this iterable is a0, ..., an. */ def flatMap[B](f: A => Iterable[B]): Iterable[B] = { val buf = new ArrayBuffer[B] val elems = elements while (elems.hasNext) f(elems.next) copyToBuffer buf buf } /** Returns all the elements of this iterable that satisfy the * predicate p. The order of the elements is preserved. * * @note Will not terminate for infinite-sized collections. * @param p the predicate used to filter the list. * @return the elements of this list satisfying p. */ def filter(p: A => Boolean): Iterable[A] = { val buf = new ArrayBuffer[A] val elems = elements while (elems.hasNext) { val x = elems.next; if (p(x)) buf += x } buf } /** Partitions this iterable in two iterables according to a predicate. * * @param p the predicate on which to partition * @return a pair of iterables: the iterable that satisfy the predicate * p and the iterable that do not. * The relative order of the elements in the resulting iterables * is the same as in the original iterable. */ def partition(p: A => Boolean): (Iterable[A], Iterable[A]) = { val matched = new ArrayBuffer[A] val failed = new ArrayBuffer[A] val elems = elements while (elems.hasNext) { val x = elems.next; if (p(x)) matched += x else failed += x } (matched, failed) } /** Returns the longest prefix of this iterable whose elements satisfy * the predicate p. * * @note May not terminate for infinite-sized collections. * @param p the test predicate. * @return the longest prefix of this iterable whose elements satisfy * the predicate p. */ def takeWhile(p: A => Boolean): Iterable[A] = (new ArrayBuffer[A] ++ elements.takeWhile(p)) /** Returns the longest suffix of this iterable whose first element * does not satisfy the predicate p. * * @note May not terminate for infinite-sized collections. * @param p the test predicate. * @return the longest suffix of the iterable whose first element * does not satisfy the predicate p. */ def dropWhile(p: A => Boolean): Collection[A] = (new ArrayBuffer[A] ++ elements.dropWhile(p)) /** Returns an iterable consisting only over the first n * elements of this iterable, or else the whole iterable, if it has less * than n elements. * * @deprecated API does not make sense for non-ordered collections * @param n the number of elements to take * @return the new iterable */ @deprecated def take(n: Int): Collection[A] = (new ArrayBuffer[A] ++ elements.take(n)) /** Returns this iterable without its n first elements * If this iterable has less than n elements, the empty * iterable is returned. * * @note Will not terminate for infinite-sized collections. * @deprecated API does not make sense for non-ordered collections * @param n the number of elements to drop * @return the new iterable */ @deprecated def drop(n: Int): Collection[A] = (new ArrayBuffer[A] ++ elements.drop(n)) /** Apply a function f to all elements of this * iterable object. * * @note Will not terminate for infinite-sized collections. * @param f a function that is applied to every element. */ def foreach(f: A => Unit): Unit = elements.foreach(f) /** Apply a predicate p to all elements of this * iterable object and return true, iff the predicate yields * true for all elements. * * @note May not terminate for infinite-sized collections. * @param p the predicate * @return true, iff the predicate yields true for all elements. */ def forall(p: A => Boolean): Boolean = elements.forall(p) /** Apply a predicate p to all elements of this * iterable object and return true, iff there is at least one * element for which p yields true. * * @note May not terminate for infinite-sized collections. * @param p the predicate * @return true, iff the predicate yields true for at least one element. */ def exists(p: A => Boolean): Boolean = elements.exists(p) /** Find and return the first element of the iterable object satisfying a * predicate, if any. * * @note may not terminate for infinite-sized collections. * @param p the predicate * @return the first element in the iterable object satisfying p, * or None if none exists. */ def find(p: A => Boolean): Option[A] = elements.find(p) /** Returns index of the first element satisying a predicate, or -1. * * @note may not terminate for infinite-sized collections. * @param p the predicate * @return the index of the first element satisfying p, * or -1 if such an element does not exist */ def findIndexOf(p: A => Boolean): Int = { val it = elements var i = 0 while (it.hasNext) if (p(it.next)) return i else i = i + 1 return -1 } /** Returns the index of the first occurence of the specified * object in this iterable object. * * @note may not terminate for infinite-sized collections. * @param elem element to search for. * @return the index in this sequence of the first occurence of the * specified element, or -1 if the sequence does not contain * this element. */ def indexOf[B >: A](elem: B): Int = { val it = elements var i = 0 var found = false while (!found && it.hasNext) { if (it.next == elem) { found = true } else { i = i + 1 } } if (found) i else -1 } /** Combines the elements of this iterable object together using the binary * function f, from left to right, and starting with * the value z. * * @note Will not terminate for infinite-sized collections. * @return f(... (f(f(z, a0), a1) ...), * an) if the list is * [a0, a1, ..., an]. */ def foldLeft[B](z: B)(op: (B, A) => B): B = elements.foldLeft(z)(op) /** Combines the elements of this list together using the binary * function f, from right to left, and starting with * the value z. * * @note Will not terminate for infinite-sized collections. * @return f(a0, f(a1, f(..., f(an, z)...))) * if the list is [a0, a1, ..., an]. */ def foldRight[B](z: B)(op: (A, B) => B): B = elements.foldRight(z)(op) /** Similar to foldLeft but can be used as * an operator with the order of list and zero arguments reversed. * That is, z /: xs is the same as xs foldLeft z * @note Will not terminate for infinite-sized collections. */ def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op) /** An alias for foldRight. * That is, xs :\ z is the same as xs foldRight z * @note Will not terminate for infinite-sized collections. */ def :\[B](z: B)(op: (A, B) => B): B = foldRight(z)(op) /** Combines the elements of this iterable object together using the binary * operator op, from left to right * @note Will not terminate for infinite-sized collections. * @param op The operator to apply * @return op(... op(a0,a1), ..., an) if the iterable object has elements * a0, a1, ..., an. * @throws Predef.UnsupportedOperationException if the iterable object is empty. */ def reduceLeft[B >: A](op: (B, A) => B): B = elements.reduceLeft(op) /** Combines the elements of this iterable object together using the binary * operator op, from right to left * @note Will not terminate for infinite-sized collections. * @param op The operator to apply * * @return a0 op (... op (an-1 op an)...) * if the iterable object has elements a0, a1, ..., * an. * * @throws Predef.UnsupportedOperationException if the iterator is empty. */ def reduceRight[B >: A](op: (A, B) => B): B = elements.reduceRight(op) /** Copy all elements to a given buffer * @note Will not terminate for infinite-sized collections. * @param dest The buffer to which elements are copied * @note Will not terminate if not finite. */ def copyToBuffer[B >: A](dest: Buffer[B]): Unit = elements copyToBuffer dest /** Checks if the other iterable object contains the same elements. * * @note will not terminate for infinite-sized collections. * @param that the other iterable object * @return true, iff both iterable objects contain the same elements. */ def sameElements[B >: A](that: Iterable[B]): Boolean = { val ita = this.elements val itb = that.elements var res = true while (res && ita.hasNext && itb.hasNext) { res = (ita.next == itb.next) } !ita.hasNext && !itb.hasNext && res } /** * Returns a list containing all of the elements in this iterable object. * @note Will not terminate for infinite-sized collections. */ def toList: List[A] = elements.toList /** * Returns a sequence containing all of the elements in this iterable object. * @note Will not terminate for infinite-sized collections. */ def toSeq: Seq[A] = toList /** * Returns a stream containing all of the elements in this iterable object. * @note consider using projection for lazy behavior. */ def toStream: Stream[A] = Stream.fromIterator(elements) /** Returns a string representation of this iterable object. The resulting string * begins with the string start and is finished by the string * end. Inside, the string representations of elements (w.r.t. * the method toString()) are separated by the string * sep. * * @ex List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)" * @note Will not terminate for infinite-sized collections. * @param start starting string. * @param sep separator string. * @param end ending string. * @return a string representation of this iterable object. */ def mkString(start: String, sep: String, end: String): String = { val buf = new StringBuilder() addString(buf, start, sep, end).toString } /** Returns a string representation of this iterable object. The string * representations of elements (w.r.t. the method toString()) * are separated by the string sep. * * @note Will not terminate for infinite-sized collections. * @param sep separator string. * @return a string representation of this iterable object. */ def mkString(sep: String): String = this.mkString("", sep, "") /** Converts a collection into a flat String by each element's toString method. * @note Will not terminate for infinite-sized collections. */ def mkString = { val buf = new StringBuilder foreach(buf append _.toString) buf.toString } /** Write all elements of this string into given string builder. * * @note Will not terminate for infinite-sized collections. * @param buf ... * @return ... */ def addString(buf: StringBuilder, start: String, sep: String, end: String): StringBuilder = { buf.append(start) val elems = elements if (elems.hasNext) buf.append(elems.next) while (elems.hasNext) { buf.append(sep); buf.append(elems.next) } buf.append(end) } def addString(buf: StringBuilder, sep: String): StringBuilder = addString(buf, "", sep, "") /** Fills the given array xs with the elements of * this sequence starting at position start. * * @note Will not terminate for infinite-sized collections. * @param xs the array to fill. * @param start starting index. * @pre the array must be large enough to hold all elements. */ def copyToArray[B >: A](xs: Array[B], start: Int): Unit = elements.copyToArray(xs, start) /** Is this collection empty? */ def isEmpty = !elements.hasNext /** * returns a projection that can be used to call non-strict filter, * map, and flatMap methods that build projections * of the collection. */ def projection : Iterable.Projection[A] = new Iterable.Projection[A] { def elements = Iterable.this.elements override def force = Iterable.this } /** returns true iff this collection has a bound size. * Many APIs in this trait will not work on collections of * unbound sizes. */ def hasDefiniteSize = true }