2011年12月19日月曜日

IterV, Enumerator

一昨日Pythonでも書いたので書いておく。

IterV

IterVはIterateeです。
ストリームをイテレートするものと考えて下さい。
ストリームを表すものとしてEnumeratorが定義されています。

IterV#applyによりEnumeratorをイテレートし、IterV#runで実行されます。

import IterV._
implicit lazy val StreamEnumerator: Enumerator[Stream] = 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))))
}
}
head(Stream(3, 4, 6)).run assert_=== Some(3)
view raw iterv1.scala hosted with ❤ by GitHub

Enumerator#applyはコンテナとIterVをとり、コンテナをイテレートする関数が定義されます。

IterV#foldは、計算が終了した場合は結果と入力をとり、計算が途中の場合は入力から次の計算を取り出す関数をとり、何らかの値を返します。

入力にはEmpty, El, EOFがあり、それぞれデータなし、データあり、データの終わりを表します。

IterVには標準でいくつかの操作が定義されています。

IterV.head

ストリームを消費して、先頭要素をOptionで返します。

IterV.peek

先頭要素を返します。
こちらはストリームを消費しません。

IterV.length

ストリームの長さを取得します。

IterV.drop

指定した数だけストリームを消費します。

IterV.collect

指定した型に変換します。

IterV.groupBy

先頭要素と各要素をとる述語が真である限り、要素を集めます。

IterV.takeWhile

述語が真のあいだ、要素を集めます。

IterV.reversed

要素の順序を逆にしたコンテナを返します。

IterV.repeat

Iterateeを繰り返し、結果を集めます。

head(Stream(1, 2, 3)).run assert_=== Some(1)
peek(Stream(1, 2, 3)).run assert_=== Some(1)
length(Stream(1, 2, 3)).run assert_=== 3
drop(2)(Stream(1, 2, 3)).run assert_=== ()
collect[Int, List].apply(Stream(1, 2, 3)).run assert_=== List(1, 2, 3)
groupBy[Int, List](_ === _).apply(Stream(1, 1, 2, 1, 1, 2)).run assert_=== List(1, 1)
takeWhile[Int, List](_ % 2 === 0).apply(Stream(2, 4, 5, 6)).run assert_=== List(2, 4)
reversed[Int, List](ListReducer)(Stream(1, 2, 3)).run assert_=== List(3, 2, 1)
repeat[Int, List[Int], List](groupBy[Int, List](_ === _)).apply(Stream(1, 1, 2, 1, 1, 2)).run assert_=== List(List(1, 1), List(2), List(1, 1), List(2))
peekDoneOr[Int, List[Int]](5.pure[List], i => takeWhile(_ lt 3))(Stream(1, 2, 3)).run assert_=== List(1, 2)
view raw iterv2.scala hosted with ❤ by GitHub

そして、Iterateeは合成が可能です。

(drop[Int](2) >|> length).apply(Stream(1, 2, 3)).run assert_=== 1
(drop[Int](1) >|> collect[Int, List]).apply(Stream(1, 2, 3)).run assert_=== List(2, 3)
(head[Int] >>= (o => head.map(_ |+| o))).apply(Stream(1, 5, 3)).run assert_=== Some(6)
view raw iterv3.scala hosted with ❤ by GitHub

scalaz.effectsパッケージではIterateeを使った入力ストリームがあります。
あまり詳しく書いていませんが、Scalaz.effectsについてを書いたので興味があったら見てください。

いまではWeb FrameworkのPlay!にも存在します。
http://playframework.github.com/api/scala/#play.api.libs.iteratee.package
今後の入出力操作の標準となるかもしれないので勉強しておいて損はないはずです。

2 件のコメント:

  1. scalaquery にもあるよ!

    https://github.com/szeiger/scala-query/blob/0.9.5/src/main/scala/org/scalaquery/iter/Iteratee.scala#L9

    返信削除
  2. おお、知りませんでした。

    すでに乱立しているかもしれないけど、Scalaz7でみなが使わざるおえない出来になることを期待しています。

    返信削除