2011年12月23日金曜日

Foldable

冬休み入ったのでのびのびひきこもりたいとおもいます。

Foldable

畳み込み関数を持ちます。
最低限実装しなければならないものはfoldRightかfoldMapです。

MA#foldMap

foldMapは要素をMonoidへ変換し、結合します。

case class ScalaChan[A](values: A*)
implicit def ScalaChanFoldable = new Foldable[ScalaChan] {
override def foldMap[A, M: Monoid](t: ScalaChan[A], f: (A) => M): M = t.values.map(f).fold(mzero[M])(_ |+| _)
}
ScalaChan(1, 2, 3, 4).foldMap(identity) assert_=== 10
view raw foldable1.scala hosted with ❤ by GitHub

MA#foldr, MA#foldl

右畳み込みと左畳み込み。

nel("Hello", "Scalaz").foldr(none[String])(_.some |+| _) assert_=== Some("HelloScalaz")
List('Scalaz, 'Scala).foldl('Scalaちゃん.wrapNel)(_ |+| _.wrapNel) assert_=== NonEmptyList('Scalaちゃん, 'Scalaz, 'Scala)
view raw foldable2.scala hosted with ❤ by GitHub

MA#foldl1, MA#foldr1

foldの初期値をなくしたもの。
失敗した場合はNoneが返る。

nil[Int].foldl1(_ * _) assert_=== None
nel(1, 2, 3).foldr1(_ * _) assert_=== Some(6)
view raw foldable3.scala hosted with ❤ by GitHub

MA#foldLeftM, MA#foldRightM

モナドに包み、合成します。

nel(3, 4, 6).foldRightM(6)((a, b) => b +>: a.wrapNel) assert_=== NonEmptyList(6, 3, 4, 3, 6, 3, 4, 3)
nel("10", "Scala").foldLeftM(10)((i, s) => s.parseInt.map(_ |+| i).toOption) assert_=== None
view raw foldable4.scala hosted with ❤ by GitHub

MA#all, MA#∀, MA#any, MA#∃, MA#element, MA#∋, MA#∈:

allとanyは述語を、elementは要素を渡します。
∀, ∃, ∋, ∈:はエイリアスです。

nel(1, 3, 5).all(_ % 2 /== 0) assert_=== true
nel(1, 3, 6) ∀ (_ % 2 /== 0) assert_=== false
none[Int].any(_ gt 0) assert_=== false
1.some ∃ (_ gt 0) assert_=== true
List(10, 20, 30).element(30) assert_=== true
List(10, 11, 12) ∋ 13 assert_=== false
11 ∈: List(10, 11, 12) assert_=== true
view raw foldable5.scala hosted with ❤ by GitHub

MA#empty, MA#count, MA#foldIndex

emptyは空かどうか、countは要素の数、foldIndexはインデックスの値を取得します。
foldIndexは失敗した場合例外を投げます。

nil[String].empty assert_=== true
nel(1, 2, 4).count assert_=== 3
100.some.foldIndex(0) assert_=== 100
view raw foldable6.scala hosted with ❤ by GitHub

MA#listl, MA#listr, MA#stream

listl, listrはListに、streamはStreamにまとめます。
listlはfoldlで、listrはfoldrで実装されています。

nel(1, 2, 3).listr assert_=== List(1, 2, 3)
'Scalaz.some.listr assert_=== List('Scalaz)
"Hello".show.stream.take(3) assert_=== Stream('H', 'H', 'H')
"Hello".some.stream.take(3) assert_=== Stream("Hello")
view raw foldable7.scala hosted with ❤ by GitHub

MA#maximum, MA#minimum

要素の中から最大値、最小値を取得します。
要素が存在しない場合、Noneが返ります。

"Scalaz".show.maximum assert_=== Some('z')
none[Int].maximum assert_=== None
nel(3, 2, 1, 2, 3).minimum assert_=== Some(1)
nil[String].minimum assert_=== None
view raw foldable8.scala hosted with ❤ by GitHub

MA#selectSplit, MA#splitWith

selectSplitは述語が真の連続する要素を集めます。
偽の要素は捨てられます。

splitWithは述語が真か偽に変わるまで要素を集めます。

nel(0, 1, 1, 2, 3, 5).selectSplit(_ % 2 /== 0) assert_=== List(List(1, 1), List(3, 5))
346.some.selectSplit(_ === 345) assert_=== Nil
import Digit._
11235L.digits.splitWith(_.toInt % 2 === 0) assert_=== List(List(_5, _3), List(_2), List(_1, _1))
"Scalaちゃん".show.splitWith(_ === 'a') assert_=== List(List('S', 'c'), List('a'), List('l'), List('a'), List('ち', 'ゃ', 'ん'))
view raw foldable9.scala hosted with ❤ by GitHub

畳み込み関数は汎用性が高いのでFoldableが定義されるだけでこれだけの関数が使えるようになります。
前に紹介したTraverseは畳み込み関数が定義されていると定義がしやすかったのですが、Foldableは継承されていません。
しかし、Scalaz7ではTraverseがFoldableを継承するように変更されています。

0 件のコメント:

コメントを投稿