Exercise:
Implement proper (means lazy) iterator for data structure B-Tree.
Code (Scala):
import org.scalatest.FunSuite
class LazyIteratorTest extends FunSuite {
trait LazyIt[T] {
def iterator(): Iterator[T]
protected def lazyIt[T](
sq: Seq[LazyIt[T]]): Iterator[T] =
sq match {
case Seq(hd) => hd.iterator
case Seq(hd, tl @ _*) =>
hd.iterator ++ lazyIt(tl)
case _ => Iterator.empty
}
}
sealed trait BTree[K, V]
extends LazyIt[(K, V)] {
type T = (K, V)
def map[R](f: T => R): Seq[R]
}
case class BTLeaf[K, V](vs: Seq[(K, V)])
extends BTree[K, V] {
override def toString =
"leaf(" + shorten(showSeq(vs)) + ")"
def iterator(): Iterator[T] = {
println("* iterator() on " + this + " *")
vs.iterator
}
def map[R](f: T => R): Seq[R] = vs.map(f)
}
case class BTNode[K, V](ts: Seq[BTree[K, V]])
extends BTree[K, V] {
override def toString =
"node(" + shorten(showSeq(ts)) + ")"
def iterator(): Iterator[T] = {
println("* iterator() on " + this + " *")
lazyIt(ts)
}
def map[R](f: T => R): Seq[R] =
ts.flatMap(_.map(f))
}
def showSeq[T](sq: Seq[T]) =
sq.map(_.toString).mkString(",")
def shorten[T](s: String) =
if (s.size <= 30) s else s.take(30) + "..."
def time = System.currentTimeMillis()
def leaf[K, V](vs: (K, V)*) = BTLeaf[K, V](vs)
def node[K, V](ts: BTree[K, V]*) = BTNode[K, V](ts)
def tree = node(
node(leaf(1 -> "a", 2 -> "b", 3 -> "c"),
leaf(4 -> "d", 5 -> "e", 6 -> "f"),
leaf(7 -> "g", 8 -> "h", 9 -> "i")),
node(leaf(10 -> "j", 11 -> "k", 12 -> "l"),
leaf(13 -> "m", 14 -> "n", 15 -> "o"),
leaf(16 -> "p", 17 -> "q", 18 -> "r")),
node(leaf(19 -> "s", 20 -> "t", 21 -> "u"),
leaf(22 -> "v", 23 -> "w", 24 -> "x"),
leaf(25 -> "y", 26 -> "z", 27 -> "!")))
test("lazy iterator") {
println("warm up before...")
tree.map(_.toString)
println("do simple map")
val t = time
println(shorten(showSeq(
tree.map(_.toString))))
println("it took time: "
+ (time - t) + " ms")
val lt = time
println("get lazy iterator {")
val it = tree.iterator()
println("} got lazy iterator")
println("do lazy take(1) {")
val t1 = it.take(1)
println("} lazy taken(1)")
println("do lazy take(2) {")
val t2 = it.take(2)
println("} lazy taken(2)")
println(t1.toList)
println(t2.toList)
println("do lazy foreach {")
it.foreach { println }
println("} done lazy foreach")
println("lazily it took time: "
+ (time - lt) + " ms")
}
}
Output:
warm up before...
do simple map
(1,a),(2,b),(3,c),(4,d),(5,e),...
it took time: 6 ms
get lazy iterator {
* iterator() on node(node(leaf((1,a),(2,b),(3,c)),l...) *
* iterator() on node(leaf((1,a),(2,b),(3,c)),leaf((...) *
* iterator() on leaf((1,a),(2,b),(3,c)) *
} got lazy iterator
do lazy take(1) {
} lazy taken(1)
do lazy take(2) {
} lazy taken(2)
List((1,a))
List((2,b), (3,c))
do lazy foreach {
* iterator() on leaf((4,d),(5,e),(6,f)) *
(4,d)
(5,e)
(6,f)
* iterator() on leaf((7,g),(8,h),(9,i)) *
(7,g)
(8,h)
(9,i)
* iterator() on node(leaf((10,j),(11,k),(12,l)),lea...) *
* iterator() on leaf((10,j),(11,k),(12,l)) *
(10,j)
(11,k)
(12,l)
* iterator() on leaf((13,m),(14,n),(15,o)) *
(13,m)
(14,n)
(15,o)
* iterator() on leaf((16,p),(17,q),(18,r)) *
(16,p)
(17,q)
(18,r)
* iterator() on node(leaf((19,s),(20,t),(21,u)),lea...) *
* iterator() on leaf((19,s),(20,t),(21,u)) *
(19,s)
(20,t)
(21,u)
* iterator() on leaf((22,v),(23,w),(24,x)) *
(22,v)
(23,w)
(24,x)
* iterator() on leaf((25,y),(26,z),(27,!)) *
(25,y)
(26,z)
(27,!)
} done lazy foreach
lazily it took time: 9 ms