Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 89 additions & 32 deletions src/main/scala/com/jsuereth/collections/View.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.jsuereth.collections

import scala.annotation.unchecked.{uncheckedVariance => uV}
import scala.collection.{GenTraversableOnce, GenTraversable}
import scala.collection.immutable.IndexedSeq
import scala.collection._
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.ArrayBuffer
import scala.language.implicitConversions


Expand All @@ -13,7 +15,7 @@ import scala.language.implicitConversions
* @tparam E The elements in the collection.
* @tparam To The resulting collection type of the staged operations acrued so far.
*/
abstract class View[E, To] {
abstract class View[E, To] extends GenTraversableOnce[E] {
/** The underlying implementation of the original collection.
*
* We need this type to have existed at one time, but we no longer care what it is.
Expand Down Expand Up @@ -103,12 +105,57 @@ abstract class View[E, To] {
}
final def reduce[A1 >: E](op: (A1, A1) => A1): A1 = reduceLeft(op)
final def aggregate[B](z: =>B)(seqop: (B, E) => B, combop: (B, B) => B): B = foldLeft(z)(seqop)
def min[B >: E](implicit cmp: Ordering[B]): E =
final def min[B >: E](implicit cmp: Ordering[B]): E =
reduceLeftOption((x, y) => if (cmp.lteq(x, y)) x else y).getOrElse(throw new UnsupportedOperationException("empty.min"))

def max[B >: E](implicit cmp: Ordering[B]): E = {
final def max[B >: E](implicit cmp: Ordering[B]): E = {
reduceLeftOption((x, y) => if (cmp.gteq(x, y)) x else y).getOrElse(throw new UnsupportedOperationException("empty.max"))
}
final def maxBy[B](f: (E) => B)(implicit cmp: Ordering[B]): E =
foldLeft[Option[(E, B)]](None) { (prev, el) =>
prev match {
case None => Some(el -> f(el))
case Some((el1, value)) =>
val newVal = f(el)
if(cmp.gteq(value, newVal)) Some(el1 -> value)
else Some(el -> newVal)
}
}.map(_._1).getOrElse(throw new UnsupportedOperationException("empty.maxBy"))
final def minBy[B](f: (E) => B)(implicit cmp: Ordering[B]): E = {
foldLeft[Option[(E, B)]](None) { (prev, el) =>
prev match {
case None => Some(el -> f(el))
case Some((el1, value)) =>
val newVal = f(el)
if(cmp.lteq(value, newVal)) Some(el1 -> value)
else Some(el -> newVal)
}
}.map(_._1).getOrElse(throw new UnsupportedOperationException("empty.minBy"))
}
final def foreach[U](f: (E) => U): Unit = foldLeft(()) { (ignore, el) => f(el) }
final def /:[B](z: B)(op: (B, E) => B): B = foldLeft(z)(op)
final def hasDefiniteSize: Boolean =
underlying.hasDefiniteSourceSize && !underlying.hasStagedOperations


final def reduceOption[A1 >: E](op: (A1, A1) => A1): Option[A1] = reduceLeftOption(op)
final def reduceRightOption[B >: E](op: (E, B) => B): Option[B] =
foldRight[Option[B]](None) { case (el, acc) =>
acc match {
case None => Some(el)
case Some(el2) => Some(op(el, el2))
}
}
final def foldRight[B](z: B)(op: (E, B) => B): B =
// TODO - maybe a faster version? We *may* be able to have the transducers support foldRight semantics...
toStream.foldRight(z)(op)
final def reduceRight[B >: E](op: (E, B) => B): B =
reduceRightOption(op).getOrElse(throw new UnsupportedOperationException("empty.reduceRight"))
final def :\[B](z: B)(op: (E, B) => B): B = foldRight(z)(op)

final def nonEmpty: Boolean =
if(hasDefiniteSize) !isEmpty
else size > 0


// TODO - less bytecode heavy mechanism here.
final def forall(p: E => Boolean): Boolean = {
Expand All @@ -129,6 +176,43 @@ abstract class View[E, To] {
final def isEmpty: Boolean = underlying.isEmpty_!
final def size: Int = underlying.size_!
final def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, E, Col[E @uV]]): Col[E @uV] = underlying.to_![Col]
final def toSet[A1 >: E]: GenSet[A1] = to[immutable.Set].asInstanceOf[GenSet[A1]]
final def toSeq: GenSeq[E] = toList

final def toBuffer[A1 >: E]: mutable.Buffer[A1] = to[ArrayBuffer].asInstanceOf[mutable.Buffer[A1]]
final def toStream: Stream[E] = toBuffer.toStream
final def toArray[A1 >: E](implicit evidence$1: ClassManifest[A1]): Array[A1] = toBuffer.toArray
final def toIterator: Iterator[E] = toBuffer.iterator
final def toVector: Vector[E] = to[Vector]
final def toList: List[E] = to[List]
final def toMap[K, V](implicit ev: <:<[E, (K, V)]): GenMap[K, V] = {
val b = immutable.Map.newBuilder[K, V]
for (x <- this) b += x
b.result()
}
final def seq: scala.TraversableOnce[E] = toBuffer
final def toIndexedSeq: IndexedSeq[E] = toVector
final def toIterable: GenIterable[E] = toBuffer
final def toTraversable: GenTraversable[E] =
// TODO - Return this
toBuffer
final def copyToArray[B >: E](xs: Array[B]): Unit = copyToArray(xs, 0, xs.length)
final def copyToArray[B >: E](xs: Array[B], start: Int): Unit = copyToArray(xs, start, xs.length - start)
final def copyToArray[B >: E](xs: Array[B], start: Int, len: Int): Unit = {
// TODO - We could probably use "take" and foreach/zipWithIndex if we wanted.
var i = start
val end = (start + len) min xs.length
import util.control.Breaks._
breakable {
for (x <- this) {
if (i >= end) break
xs(i) = x
i += 1
}
}
}



// SUmmation/addition things.
final def sum[B >: E](implicit num: Numeric[B]): B = foldLeft(num.zero)(num.plus)
Expand All @@ -137,35 +221,8 @@ abstract class View[E, To] {

final def mkString(start: String, sep: String, end: String): String =
addString(new StringBuilder(), start, sep, end).toString

final def mkString(sep: String): String = mkString("", sep, "")

final def mkString: String = mkString("")

/** 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`.
*
* Example:
*
* {{{
* scala> val a = List(1,2,3,4)
* a: List[Int] = List(1, 2, 3, 4)
*
* scala> val b = new StringBuilder()
* b: StringBuilder =
*
* scala> a.addString(b , "List(" , ", " , ")")
* res5: StringBuilder = List(1, 2, 3, 4)
* }}}
*
* @param b 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.
*/
def addString(b: StringBuilder, start: String, sep: String, end: String): StringBuilder = {
var first = true
b append start
Expand Down
8 changes: 8 additions & 0 deletions src/test/scala/com/jsuereth/collections/ViewSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ object ViewSpec extends Specification {
take init $init
forall test $forall
exists test $exists
flatMap elements $flatMap
"""

def count = {
Expand All @@ -40,6 +41,13 @@ object ViewSpec extends Specification {
ourView must beEqualTo(scalaView)
}

def flatMap = {
val orig = Vector(1,2,3)
val scalaView = orig.view.flatMap(x => (1 to x).toVector.view).force
val ourView = orig.stagedView.flatMap(x => (1 to x).toVector.stagedView).force
scalaView must beEqualTo(ourView)
}

def drop = {
val orig = (1 to 20).to[scala.collection.mutable.ArrayBuffer]
val scalaView = orig.view.drop(10).force
Expand Down