if を追放する
Kコンビネータと真偽値に関する話題が挙がっていたので、それを使って何か書いてみようと思いました。
条件分岐と言えば FizzBuzz。以下のようなプログラムを考えます。
main = mapM putStrLn [ max (show n) $ fizz n ++ buzz n | n <- [1..100] ] fizz n = if mon n 3 == 0 then "Fizz" else "" buzz n = if mon n 5 == 0 then "Buzz" else ""
まずは以下のように if' を定義しますが、これだけだとまだあまり変わっていません。
Haskell 標準の Bool 型を使っているし、パターンマッチで結局 if 相当のことをやっているからです。(ポイントフリーで書けない!!)
fizz n = if' (mod n 3 == 0) "Fizz" "" buzz n = if' (mod n 5 == 0) "Buzz" "" if' True = const if' False = const id
なので、数値型から直接条件分岐することを考えます。リストを使ってこんな感じ。
fizz n = if' (mod n 3) "Fizz" "" buzz n = if' (mod n 5) "Buzz" "" if' b = foldr id const $ take b [flip]
foldl を使えば、False = flip const ではなく False = const id で定義することもできますが、型推論が上手くいかないので流行りの Unsafe.Coerce が必要になります。
import Unsafe.Coerce if' b = foldl (unsafeCoerce id) const $ take b [id]
一般的には 0 が False、0 以外が True かな。そうすると↓のような感じ。
fizz n = if' (mod n 3) "" "Fizz" buzz n = if' (mod n 5) "" "Buzz" if' b = foldr id (flip const) $ take b [flip]
うん、とりあえず if や case が無くてもプログラムは組めそう。
ちなみに if-then-else という糖衣構文をやめて、Prelude に if' 関数を追加しようということを主張しているページはこちら。
http://www.haskell.org/haskellwiki/If-then-else