2012年2月11日土曜日

Scalaでポイントフリースタイル

関数合成の話がTLであったので書いてみる。

最近だとゆろよろさんの記事が面白い。
http://d.hatena.ne.jp/yuroyoro/20120203/1328248662
http://d.hatena.ne.jp/yuroyoro/20120206/1328513534

Category

とりあえずCategoryから。

Categoryとか知らない人は
https://github.com/quassia88/introduction-to-category-theory-in-scala-jp/wiki
を見て、なんとなく分かってみてください。

この圏論入門の記事だとCategoryのインスタンスはFunctionしか書いてなくて、抽象化をしていることがわかりにくいですが、ScalazではCategoryのインスタンスにFunction, PartialFunction, Kleisliがあります。

※コード例はScalaz7

import scalaz.std.function._
function1Instance.id(5) assert_=== 5
lazy val show: Int => String = _.shows
lazy val len: String => Int = _.length
lazy val f: Int => Int = function1Instance.compose(len, show)
f(100) assert_=== 3
view raw category1.scala hosted with ❤ by GitHub

idが恒等関数、composeが合成関数です。

>>>, <<<というシンタックスもあります。

(len <<< show)(20) assert_=== 2
(len >>> show)("100") assert_=== "3"
view raw category2.scala hosted with ❤ by GitHub

Arrow

究極的には関数合成だけでプログラムを書くこともできますが、>>>だけではなかなか難しいところがあります。

そこで登場するものが***と&&&。
この2つは関数を並列に合成します。

***はA => BとC => Dから(A, C) => (B, D)を返します。
&&&はA => BとA => CからA => (B, C)を返します。

(show *** len)(5, "hello") assert_=== ("5", 5)
(show &&& (show >>> len))(100) assert_=== ("100", 3)
view raw arr.scala hosted with ❤ by GitHub


関数合成を意識して、n番目のフィボナッチ数を得る関数を書いてみます。

lazy val fst: ((Int, Int)) => Int = _._1
lazy val snd: ((Int, Int)) => Int = _._2
lazy val add: ((Int, Int)) => Int = _.fold(_ |+| _)
lazy val pair: ((Int, Int)) => (Int, Int) = snd &&& add
lazy val recur: Int => ((Int, Int)) => (Int, Int) = {
case 0 => identity
case n => (n - 1 |> recur) >>> pair
}
lazy val init: (((Int, Int)) => (Int, Int)) => (Int, Int) = (0, 1) |> _
lazy val fib: Int => Int = recur >>> init >>> fst
(0 to 9).map(fib).toList assert_=== List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)
view raw fib.scala hosted with ❤ by GitHub

|>はパイプ演算子と言われるもので、値を関数に適用するものです。

並列合成の使いどころは、ある値を複数回使うときです。

ScalazではTupleでfoldが使えるので別々に計算したものを簡単にまとめることが出来ます。

Scalaでも、Let's point free!

0 件のコメント:

コメントを投稿