Life Goes On

まあまあだけど楽しんでる方です

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