/* __ *\
** ________ ___ / / ___ 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
}