Scala で Iteratee と Enumerator を書く練習
とりあえず使い方だけメモ
package com.github.cooldaemon.scalaz_test import scalaz._ object TryIteratee { import Scalaz._ import IterV._ def run { implicit val ListEnum = new Enumerator[Seq] { def apply[E, A](e: Seq[E], i: IterV[E, A]): IterV[E, A] = { e match { case Seq() => i case x::xs => i.fold( done = (_, _) => i, cont = k => apply(xs, k(El(x))) ) } } } implicit val StreamEnum = new Enumerator[Stream] { def apply[E, A](e: Stream[E], i: IterV[E, A]): IterV[E, A] = { e match { case Stream() => i case x #:: xs => i.fold( done = (_, _) => i, cont = k => apply(xs, k(El(x))) ) } } } val seq = Seq.range(1, 5) head(seq).run assert_=== Some(1) val m = for { x <- head[Int] y <- head[Int] } yield (x, y) m(seq).run assert_=== (Some(1), Some(2)) def dropAndPrintAll[E] :IterV[E, Unit] = { def step(s: Input[E]) :IterV[E, Unit] = { s( el = {e => println(e) Cont(step) }, empty = Cont(step), eof = Done((), EOF[E]) ) } Cont(step) } println("--<自作 Iteratee>--") dropAndPrintAll(seq).run def peekAndPrintAll[E] :IterV[E, Unit] = { def step(initS: Input[E])(s: Input[E]): IterV[E, Unit] = { s( el = {e => println(e) Cont(step(initS)) }, empty = Cont(step(initS)), eof = Done((), initS) ) } Cont({s => step(s)(s)}) } val m2 = for { _ <- peekAndPrintAll[Int] x <- head[Int] } yield x println("--<Iteratee の合成>--") m2(seq).run assert_=== Some(1) println("--<Enumerator を後から追加>--") val m3 = dropAndPrintAll(seq) val m4 = m3(Stream.range(5, 10)) // Seq に Stream を追加しても良い m4.run } }
解説は後で。例とは言え Iteratee の中に副作用が存在する所がダサい。
疑問点その1
下記を末尾再帰にするには…
def apply[E, A](e: Seq[E], i: IterV[E, A]): IterV[E, A] = { e match { case Seq() => i case x::xs => i.fold( done = (_, _) => i, cont = k => apply(xs, k(El(x))) ) } }
下記のようにする必要がある。何でだろー?
@annotation.tailrec def apply[E, A](e: Seq[E], i: IterV[E, A]): IterV[E, A] = { val next: Option[(Seq[E], IterV[E, A])] = e match { case Seq() => None case x::xs => i.fold( done = (_, _) => None, cont = k => some((xs, k(El(x)))) ) } next match { case None => i case Some((es, is)) => apply(es, is) } }
疑問点その2
Haskell みたいに Enumerator 同士を合成する方法が解らない