added immutable persistent Map and Vector
This commit is contained in:
parent
0f6ca61cb2
commit
7abd91785c
2 changed files with 712 additions and 0 deletions
361
kernel/src/main/scala/collection/HashTrie.scala
Executable file
361
kernel/src/main/scala/collection/HashTrie.scala
Executable file
|
|
@ -0,0 +1,361 @@
|
|||
/**
|
||||
Copyright (c) 2007-2008, Rich Hickey
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of Clojure nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
**/
|
||||
|
||||
package se.scalablesolutions.akka.collection
|
||||
|
||||
/**
|
||||
* A clean-room port of Rich Hickey's persistent hash trie implementation from
|
||||
* Clojure (http://clojure.org). Originally presented as a mutable structure in
|
||||
* a paper by Phil Bagwell.
|
||||
*
|
||||
* @author Daniel Spiewak
|
||||
* @author Rich Hickey
|
||||
*/
|
||||
final class HashTrie[K, +V] private (root: Node[K, V]) extends Map[K, V] {
|
||||
lazy val size = root.size
|
||||
|
||||
def this() = this(new EmptyNode[K])
|
||||
|
||||
def get(key: K) = root(key, key.hashCode)
|
||||
|
||||
override def +[A >: V](pair: (K, A)) = pair match {
|
||||
case (k, v) => update(k, v)
|
||||
}
|
||||
|
||||
def update[A >: V](key: K, value: A) = new HashTrie(root(0, key, key.hashCode) = value)
|
||||
|
||||
def -(key: K) = new HashTrie(root.remove(key, key.hashCode))
|
||||
|
||||
def elements = root.elements
|
||||
|
||||
def empty[A]: HashTrie[K, A] = new HashTrie(new EmptyNode[K])
|
||||
|
||||
def diagnose = root.toString
|
||||
}
|
||||
|
||||
object HashTrie {
|
||||
def apply[K, V](pairs: (K, V)*) = pairs.foldLeft(new HashTrie[K, V]) { _ + _ }
|
||||
|
||||
def unapplySeq[K, V](map: HashTrie[K, V]) = map.toSeq
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// nodes
|
||||
|
||||
private[collection] sealed trait Node[K, +V] {
|
||||
val size: Int
|
||||
|
||||
def apply(key: K, hash: Int): Option[V]
|
||||
|
||||
def update[A >: V](shift: Int, key: K, hash: Int, value: A): Node[K, A]
|
||||
|
||||
def remove(key: K, hash: Int): Node[K, V]
|
||||
|
||||
def elements: Iterator[(K, V)]
|
||||
}
|
||||
|
||||
|
||||
private[collection] class EmptyNode[K] extends Node[K, Nothing] {
|
||||
val size = 0
|
||||
|
||||
def apply(key: K, hash: Int) = None
|
||||
|
||||
def update[V](shift: Int, key: K, hash: Int, value: V) = new LeafNode(key, hash, value)
|
||||
|
||||
def remove(key: K, hash: Int) = this
|
||||
|
||||
lazy val elements = new Iterator[(K, Nothing)] {
|
||||
val hasNext = false
|
||||
|
||||
val next = null
|
||||
}
|
||||
}
|
||||
|
||||
private[collection] abstract class SingleNode[K, +V] extends Node[K, V] {
|
||||
val hash: Int
|
||||
}
|
||||
|
||||
|
||||
private[collection] class LeafNode[K, +V](key: K, val hash: Int, value: V) extends SingleNode[K, V] {
|
||||
val size = 1
|
||||
|
||||
def apply(key: K, hash: Int) = if (this.key == key) Some(value) else None
|
||||
|
||||
def update[A >: V](shift: Int, key: K, hash: Int, value: A) = {
|
||||
if (this.key == key) {
|
||||
if (this.value == value) this else new LeafNode(key, hash, value)
|
||||
} else if (this.hash == hash) {
|
||||
new CollisionNode(hash, this.key -> this.value, key -> value)
|
||||
} else {
|
||||
BitmappedNode(shift)(this, key, hash, value)
|
||||
}
|
||||
}
|
||||
|
||||
def remove(key: K, hash: Int) = if (this.key == key) new EmptyNode[K] else this
|
||||
|
||||
def elements = new Iterator[(K, V)] {
|
||||
var hasNext = true
|
||||
|
||||
def next = {
|
||||
hasNext = false
|
||||
(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
override def toString = "LeafNode(" + key + " -> " + value + ")"
|
||||
}
|
||||
|
||||
|
||||
private[collection] class CollisionNode[K, +V](val hash: Int, bucket: List[(K, V)]) extends SingleNode[K, V] {
|
||||
lazy val size = bucket.length
|
||||
|
||||
def this(hash: Int, pairs: (K, V)*) = this(hash, pairs.toList)
|
||||
|
||||
def apply(key: K, hash: Int) = {
|
||||
for {
|
||||
(_, v) <- bucket find { case (k, _) => k == key }
|
||||
} yield v
|
||||
}
|
||||
|
||||
def update[A >: V](shift: Int, key: K, hash: Int, value: A): Node[K, A] = {
|
||||
if (this.hash == hash) {
|
||||
var found = false
|
||||
|
||||
val newBucket = for ((k, v) <- bucket) yield {
|
||||
if (k == key) {
|
||||
found = true
|
||||
(key, value)
|
||||
} else (k, v)
|
||||
}
|
||||
|
||||
new CollisionNode(hash, if (found) newBucket else (key, value) :: bucket)
|
||||
} else {
|
||||
BitmappedNode(shift)(this, key, hash, value)
|
||||
}
|
||||
}
|
||||
|
||||
def remove(key: K, hash: Int) = {
|
||||
val newBucket = bucket filter { case (k, _) => k != key }
|
||||
|
||||
if (newBucket.length == bucket.length) this else {
|
||||
if (newBucket.length == 1) {
|
||||
val (key, value) = newBucket.head
|
||||
new LeafNode(key, hash, value)
|
||||
} else new CollisionNode(hash, newBucket)
|
||||
}
|
||||
}
|
||||
|
||||
def elements = bucket.elements
|
||||
|
||||
override def toString = "CollisionNode(" + bucket.toString + ")"
|
||||
}
|
||||
|
||||
|
||||
private[collection] class BitmappedNode[K, +V](shift: Int)(table: Array[Node[K, V]], bits: Int) extends Node[K, V] {
|
||||
lazy val size = {
|
||||
val sizes = for {
|
||||
n <- table
|
||||
if n != null
|
||||
} yield n.size
|
||||
|
||||
sizes.foldLeft(0) { _ + _ }
|
||||
}
|
||||
|
||||
def apply(key: K, hash: Int) = {
|
||||
val i = (hash >>> shift) & 0x01f
|
||||
val mask = 1 << i
|
||||
|
||||
if ((bits & mask) == mask) table(i)(key, hash) else None
|
||||
}
|
||||
|
||||
def update[A >: V](levelShift: Int, key: K, hash: Int, value: A): Node[K, A] = {
|
||||
val i = (hash >>> shift) & 0x01f
|
||||
val mask = 1 << i
|
||||
|
||||
if ((bits & mask) == mask) {
|
||||
val node = (table(i)(shift + 5, key, hash) = value)
|
||||
|
||||
if (node == table(i)) this else {
|
||||
val newTable = new Array[Node[K, A]](table.length)
|
||||
Array.copy(table, 0, newTable, 0, table.length)
|
||||
|
||||
newTable(i) = node
|
||||
|
||||
new BitmappedNode(shift)(newTable, bits)
|
||||
}
|
||||
} else {
|
||||
val newTable = new Array[Node[K, A]](Math.max(table.length, i + 1))
|
||||
Array.copy(table, 0, newTable, 0, table.length)
|
||||
|
||||
newTable(i) = new LeafNode(key, hash, value)
|
||||
|
||||
val newBits = bits | mask
|
||||
if (newBits == ~0) {
|
||||
new FullNode(shift)(newTable)
|
||||
} else {
|
||||
new BitmappedNode(shift)(newTable, newBits)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def remove(key: K, hash: Int) = {
|
||||
val i = (hash >>> shift) & 0x01f
|
||||
val mask = 1 << i
|
||||
|
||||
if ((bits & mask) == mask) {
|
||||
val node = table(i).remove(key, hash)
|
||||
|
||||
if (node == table(i)) {
|
||||
this
|
||||
} else if (node.isInstanceOf[EmptyNode[_]]) {
|
||||
if (size == 1) new EmptyNode[K] else {
|
||||
val adjustedBits = bits ^ mask
|
||||
val log = Math.log(adjustedBits) / Math.log(2)
|
||||
|
||||
if (log.toInt.toDouble == log) { // last one
|
||||
table(log.toInt)
|
||||
} else {
|
||||
val newTable = new Array[Node[K, V]](table.length)
|
||||
Array.copy(table, 0, newTable, 0, newTable.length)
|
||||
|
||||
newTable(i) = null
|
||||
|
||||
new BitmappedNode(shift)(newTable, adjustedBits)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val newTable = new Array[Node[K, V]](table.length)
|
||||
Array.copy(table, 0, newTable, 0, table.length)
|
||||
|
||||
newTable(i) = node
|
||||
|
||||
new BitmappedNode(shift)(newTable, bits)
|
||||
}
|
||||
} else this
|
||||
}
|
||||
|
||||
def elements = {
|
||||
table.foldLeft(emptyElements) { (it, e) =>
|
||||
if (e == null) it else it ++ e.elements
|
||||
}
|
||||
}
|
||||
|
||||
override def toString = "BitmappedNode(" + size + "," + table.filter(_ != null).toList.toString + ")"
|
||||
|
||||
private lazy val emptyElements: Iterator[(K, V)] = new Iterator[(K, V)] {
|
||||
val hasNext = false
|
||||
|
||||
val next = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private[collection] object BitmappedNode {
|
||||
def apply[K, V](shift: Int)(node: SingleNode[K, V], key: K, hash: Int, value: V) = {
|
||||
val table = new Array[Node[K, V]](Math.max((hash >>> shift) & 0x01f, (node.hash >>> shift) & 0x01f) + 1)
|
||||
|
||||
val preBits = {
|
||||
val i = (node.hash >>> shift) & 0x01f
|
||||
table(i) = node
|
||||
1 << i
|
||||
}
|
||||
|
||||
val bits = {
|
||||
val i = (hash >>> shift) & 0x01f
|
||||
val mask = 1 << i
|
||||
|
||||
if ((preBits & mask) == mask) {
|
||||
table(i) = (table(i)(shift + 5, key, hash) = value)
|
||||
} else {
|
||||
table(i) = new LeafNode(key, hash, value)
|
||||
}
|
||||
|
||||
preBits | mask
|
||||
}
|
||||
|
||||
new BitmappedNode(shift)(table, bits)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private[collection] class FullNode[K, +V](shift: Int)(table: Array[Node[K, V]]) extends Node[K, V] {
|
||||
lazy val size = table.foldLeft(0) { _ + _.size }
|
||||
|
||||
def apply(key: K, hash: Int) = table((hash >>> shift) & 0x01f)(key, hash)
|
||||
|
||||
def update[A >: V](levelShift: Int, key: K, hash: Int, value: A) = {
|
||||
val i = (hash >>> shift) & 0x01f
|
||||
|
||||
val node = (table(i)(shift + 5, key, hash) = value)
|
||||
|
||||
if (node == table(i)) this else {
|
||||
val newTable = new Array[Node[K, A]](32)
|
||||
Array.copy(table, 0, newTable, 0, 32)
|
||||
|
||||
newTable(i) = node
|
||||
|
||||
new FullNode(shift)(newTable)
|
||||
}
|
||||
}
|
||||
|
||||
def remove(key: K, hash: Int) = {
|
||||
val i = (hash >>> shift) & 0x01f
|
||||
val mask = 1 << i
|
||||
|
||||
val node = table(i).remove(key, hash)
|
||||
|
||||
if (node == table(i)) this else {
|
||||
val newTable = new Array[Node[K, V]](32)
|
||||
Array.copy(table, 0, newTable, 0, 32)
|
||||
|
||||
if (node.isInstanceOf[EmptyNode[_]]) {
|
||||
newTable(i) = null
|
||||
new BitmappedNode(shift)(newTable, ~mask)
|
||||
} else {
|
||||
newTable(i) = node
|
||||
new FullNode(shift)(newTable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def elements = table.foldLeft(emptyElements) { _ ++ _.elements }
|
||||
|
||||
override def toString = "FullNode(" + table.foldLeft("") { _.toString + ", " + _.toString } + ")"
|
||||
|
||||
private lazy val emptyElements: Iterator[(K, V)] = new Iterator[(K, V)] {
|
||||
val hasNext = false
|
||||
|
||||
val next = null
|
||||
}
|
||||
}
|
||||
351
kernel/src/main/scala/collection/Vector.scala
Executable file
351
kernel/src/main/scala/collection/Vector.scala
Executable file
|
|
@ -0,0 +1,351 @@
|
|||
/**
|
||||
Copyright (c) 2007-2008, Rich Hickey
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of Clojure nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
**/
|
||||
|
||||
package se.scalablesolutions.akka.collection
|
||||
|
||||
import Vector._
|
||||
|
||||
/**
|
||||
* A straight port of Clojure's <code>PersistentVector</code> class.
|
||||
*
|
||||
* @author Daniel Spiewak
|
||||
* @author Rich Hickey
|
||||
*/
|
||||
class Vector[+T] private (val length: Int, shift: Int, root: Array[AnyRef], tail: Array[AnyRef]) extends RandomAccessSeq[T] { outer =>
|
||||
private val tailOff = length - tail.length
|
||||
|
||||
/*
|
||||
* The design of this data structure inherantly requires heterogenous arrays.
|
||||
* It is *possible* to design around this, but the result is comparatively
|
||||
* quite inefficient. With respect to this fact, I have left the original
|
||||
* (somewhat dynamically-typed) implementation in place.
|
||||
*/
|
||||
|
||||
private[collection] def this() = this(0, 5, EmptyArray, EmptyArray)
|
||||
|
||||
def apply(i: Int): T = {
|
||||
if (i >= 0 && i < length) {
|
||||
if (i >= tailOff) {
|
||||
tail(i & 0x01f).asInstanceOf[T]
|
||||
} else {
|
||||
var arr = root
|
||||
var level = shift
|
||||
|
||||
while (level > 0) {
|
||||
arr = arr((i >>> level) & 0x01f).asInstanceOf[Array[AnyRef]]
|
||||
level -= 5
|
||||
}
|
||||
|
||||
arr(i & 0x01f).asInstanceOf[T]
|
||||
}
|
||||
} else throw new IndexOutOfBoundsException(i.toString)
|
||||
}
|
||||
|
||||
def update[A >: T](i: Int, obj: A): Vector[A] = {
|
||||
if (i >= 0 && i < length) {
|
||||
if (i >= tailOff) {
|
||||
val newTail = new Array[AnyRef](tail.length)
|
||||
Array.copy(tail, 0, newTail, 0, tail.length)
|
||||
newTail(i & 0x01f) = obj.asInstanceOf[AnyRef]
|
||||
|
||||
new Vector[A](length, shift, root, newTail)
|
||||
} else {
|
||||
new Vector[A](length, shift, doAssoc(shift, root, i, obj), tail)
|
||||
}
|
||||
} else if (i == length) {
|
||||
this + obj
|
||||
} else throw new IndexOutOfBoundsException(i.toString)
|
||||
}
|
||||
|
||||
private def doAssoc[A >: T](level: Int, arr: Array[AnyRef], i: Int, obj: A): Array[AnyRef] = {
|
||||
val ret = new Array[AnyRef](arr.length)
|
||||
Array.copy(arr, 0, ret, 0, arr.length)
|
||||
|
||||
if (level == 0) {
|
||||
ret(i & 0x01f) = obj.asInstanceOf[AnyRef]
|
||||
} else {
|
||||
val subidx = (i >>> level) & 0x01f
|
||||
ret(subidx) = doAssoc(level - 5, arr(subidx).asInstanceOf[Array[AnyRef]], i, obj)
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
override def ++[A >: T](other: Iterable[A]) = other.foldLeft(this:Vector[A]) { _ + _ }
|
||||
|
||||
def +[A >: T](obj: A): Vector[A] = {
|
||||
if (tail.length < 32) {
|
||||
val newTail = new Array[AnyRef](tail.length + 1)
|
||||
Array.copy(tail, 0, newTail, 0, tail.length)
|
||||
newTail(tail.length) = obj.asInstanceOf[AnyRef]
|
||||
|
||||
new Vector[A](length + 1, shift, root, newTail)
|
||||
} else {
|
||||
var (newRoot, expansion) = pushTail(shift - 5, root, tail, null)
|
||||
var newShift = shift
|
||||
|
||||
if (expansion != null) {
|
||||
newRoot = array(newRoot, expansion)
|
||||
newShift += 5
|
||||
}
|
||||
|
||||
new Vector[A](length + 1, newShift, newRoot, array(obj.asInstanceOf[AnyRef]))
|
||||
}
|
||||
}
|
||||
|
||||
private def pushTail(level: Int, arr: Array[AnyRef], tailNode: Array[AnyRef], expansion: AnyRef): (Array[AnyRef], AnyRef) = {
|
||||
val newChild = if (level == 0) tailNode else {
|
||||
val (newChild, subExpansion) = pushTail(level - 5, arr(arr.length - 1).asInstanceOf[Array[AnyRef]], tailNode, expansion)
|
||||
|
||||
if (subExpansion == null) {
|
||||
val ret = new Array[AnyRef](arr.length)
|
||||
Array.copy(arr, 0, ret, 0, arr.length)
|
||||
|
||||
ret(arr.length - 1) = newChild
|
||||
|
||||
return (ret, null)
|
||||
} else subExpansion
|
||||
}
|
||||
|
||||
// expansion
|
||||
if (arr.length == 32) {
|
||||
(arr, array(newChild))
|
||||
} else {
|
||||
val ret = new Array[AnyRef](arr.length + 1)
|
||||
Array.copy(arr, 0, ret, 0, arr.length)
|
||||
ret(arr.length) = newChild
|
||||
|
||||
(ret, null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the <i>tail</i> element of this vector.
|
||||
*/
|
||||
def pop: Vector[T] = {
|
||||
if (length == 0) {
|
||||
throw new IllegalStateException("Can't pop empty vector")
|
||||
} else if (length == 1) {
|
||||
EmptyVector
|
||||
} else if (tail.length > 1) {
|
||||
val newTail = new Array[AnyRef](tail.length - 1)
|
||||
Array.copy(tail, 0, newTail, 0, newTail.length)
|
||||
|
||||
new Vector[T](length - 1, shift, root, newTail)
|
||||
} else {
|
||||
var (newRoot, pTail) = popTail(shift - 5, root, null)
|
||||
var newShift = shift
|
||||
|
||||
if (newRoot == null) {
|
||||
newRoot = EmptyArray
|
||||
}
|
||||
|
||||
if (shift > 5 && newRoot.length == 1) {
|
||||
newRoot = newRoot(0).asInstanceOf[Array[AnyRef]]
|
||||
newShift -= 5
|
||||
}
|
||||
|
||||
new Vector[T](length - 1, newShift, newRoot, pTail.asInstanceOf[Array[AnyRef]])
|
||||
}
|
||||
}
|
||||
|
||||
private def popTail(shift: Int, arr: Array[AnyRef], pTail: AnyRef): (Array[AnyRef], AnyRef) = {
|
||||
val newPTail = if (shift > 0) {
|
||||
val (newChild, subPTail) = popTail(shift - 5, arr(arr.length - 1).asInstanceOf[Array[AnyRef]], pTail)
|
||||
|
||||
if (newChild != null) {
|
||||
val ret = new Array[AnyRef](arr.length)
|
||||
Array.copy(arr, 0, ret, 0, arr.length)
|
||||
|
||||
ret(arr.length - 1) = newChild
|
||||
|
||||
return (ret, subPTail)
|
||||
}
|
||||
subPTail
|
||||
} else if (shift == 0) {
|
||||
arr(arr.length - 1)
|
||||
} else pTail
|
||||
|
||||
// contraction
|
||||
if (arr.length == 1) {
|
||||
(null, newPTail)
|
||||
} else {
|
||||
val ret = new Array[AnyRef](arr.length - 1)
|
||||
Array.copy(arr, 0, ret, 0, ret.length)
|
||||
|
||||
(ret, newPTail)
|
||||
}
|
||||
}
|
||||
|
||||
override def filter(p: (T)=>Boolean) = {
|
||||
var back = new Vector[T]
|
||||
var i = 0
|
||||
|
||||
while (i < length) {
|
||||
val e = apply(i)
|
||||
if (p(e)) back += e
|
||||
|
||||
i += 1
|
||||
}
|
||||
|
||||
back
|
||||
}
|
||||
|
||||
override def flatMap[A](f: (T)=>Iterable[A]) = {
|
||||
var back = new Vector[A]
|
||||
var i = 0
|
||||
|
||||
while (i < length) {
|
||||
f(apply(i)) foreach { back += _ }
|
||||
i += 1
|
||||
}
|
||||
|
||||
back
|
||||
}
|
||||
|
||||
override def map[A](f: (T)=>A) = {
|
||||
var back = new Vector[A]
|
||||
var i = 0
|
||||
|
||||
while (i < length) {
|
||||
back += f(apply(i))
|
||||
i += 1
|
||||
}
|
||||
|
||||
back
|
||||
}
|
||||
|
||||
override def reverse: Vector[T] = new VectorProjection[T] {
|
||||
override val length = outer.length
|
||||
|
||||
override def apply(i: Int) = outer.apply(length - i - 1)
|
||||
}
|
||||
|
||||
override def subseq(from: Int, end: Int) = subVector(from, end)
|
||||
|
||||
def subVector(from: Int, end: Int): Vector[T] = {
|
||||
if (from < 0) {
|
||||
throw new IndexOutOfBoundsException(from.toString)
|
||||
} else if (end >= length) {
|
||||
throw new IndexOutOfBoundsException(end.toString)
|
||||
} else if (end <= from) {
|
||||
throw new IllegalArgumentException("Invalid range: " + from + ".." + end)
|
||||
} else {
|
||||
new VectorProjection[T] {
|
||||
override val length = end - from
|
||||
|
||||
override def apply(i: Int) = outer.apply(i + from)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def zip[A](that: Vector[A]) = {
|
||||
var back = new Vector[(T, A)]
|
||||
var i = 0
|
||||
|
||||
val limit = Math.min(length, that.length)
|
||||
while (i < limit) {
|
||||
back += (apply(i), that(i))
|
||||
i += 1
|
||||
}
|
||||
|
||||
back
|
||||
}
|
||||
|
||||
def zipWithIndex = {
|
||||
var back = new Vector[(T, Int)]
|
||||
var i = 0
|
||||
|
||||
while (i < length) {
|
||||
back += (apply(i), i)
|
||||
i += 1
|
||||
}
|
||||
|
||||
back
|
||||
}
|
||||
|
||||
override def equals(other: Any) = other match {
|
||||
case vec:Vector[T] => {
|
||||
var back = length == vec.length
|
||||
var i = 0
|
||||
|
||||
while (i < length) {
|
||||
back &&= apply(i) == vec.apply(i)
|
||||
i += 1
|
||||
}
|
||||
|
||||
back
|
||||
}
|
||||
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def hashCode = foldLeft(0) { _ ^ _.hashCode }
|
||||
}
|
||||
|
||||
object Vector {
|
||||
private[collection] val EmptyArray = new Array[AnyRef](0)
|
||||
|
||||
def apply[T](elems: T*) = elems.foldLeft(EmptyVector:Vector[T]) { _ + _ }
|
||||
|
||||
def unapplySeq[T](vec: Vector[T]): Option[Seq[T]] = Some(vec)
|
||||
|
||||
@inline
|
||||
private[collection] def array(elems: AnyRef*) = {
|
||||
val back = new Array[AnyRef](elems.length)
|
||||
Array.copy(elems, 0, back, 0, back.length)
|
||||
|
||||
back
|
||||
}
|
||||
}
|
||||
|
||||
object EmptyVector extends Vector[Nothing]
|
||||
|
||||
private[collection] abstract class VectorProjection[+T] extends Vector[T] {
|
||||
override val length: Int
|
||||
override def apply(i: Int): T
|
||||
|
||||
override def +[A >: T](e: A) = innerCopy + e
|
||||
|
||||
override def update[A >: T](i: Int, e: A) = {
|
||||
if (i < 0) {
|
||||
throw new IndexOutOfBoundsException(i.toString)
|
||||
} else if (i > length) {
|
||||
throw new IndexOutOfBoundsException(i.toString)
|
||||
} else innerCopy(i) = e
|
||||
}
|
||||
|
||||
private lazy val innerCopy = foldLeft(EmptyVector:Vector[T]) { _ + _ }
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue