2009-02-04
Haskell のデータ構築子
Haskell の代数データ型で使われるデータ構築子は、実は関数と同様に扱えます。たとえば、四則演算の式を表す代数データ型を以下のように定義したとします。
data Expr = C Int | Add Expr Expr | Sub Expr Expr | Mul Expr Expr | Div Expr Expr deriving Show
"1 + 2" は "Add (C 1) (C 2)" と表現できます。この式を評価してみましょう。
Add (C 1) (C 2) → Add (C 1) (C 2)
当たり前ですが、そのものが返ってきます。
話は変わりますが、add という関数を素直に定義すると、こうなるでしょう。
add x y = x + y
式 "add 1 2" を評価するとこうなります。
add 1 2 → 3
すなわち、関数であれば簡略化されますが、データ構築子であれば簡略化されないところだけが違います。
全置表現(あるいは Lisp もどき)
Expr を実際に計算するプログラムを書きましょう。
data Expr = C Int | Add Expr Expr | Sub Expr Expr | Mul Expr Expr | Div Expr Expr deriving Show evaluate :: Expr -> Int evaluate (C x) = x evaluate (Add e1 e2) = evaluate e1 + evaluate e2 evaluate (Sub e1 e2) = evaluate e1 - evaluate e2 evaluate (Mul e1 e2) = evaluate e1 * evaluate e2 evaluate (Div e1 e2) = evaluate e1 `div` evaluate e2
(10 + 8 / 2 * 7 - 4) に相当する Expr を evaluate の引数に指定すると、こうなります。
evaluate (Sub (Add (C 10) (Mul (Div (C 8) (C 2)) (C 7))) (C 4)) → 34
二項演算子1
データ構築子は関数のように扱えるので、バッククートで囲むと二項演算子になります。
data Expr = C Int | Expr `Add` Expr | Expr `Sub` Expr | Expr `Mul` Expr | Expr `Div` Expr deriving Show evaluate :: Expr -> Int evaluate (C x) = x evaluate (e1 `Add` e2) = evaluate e1 + evaluate e2 evaluate (e1 `Sub` e2) = evaluate e1 - evaluate e2 evaluate (e1 `Mul` e2) = evaluate e1 * evaluate e2 evaluate (e1 `Div` e2) = evaluate e1 `div` evaluate e2
こうすると、二項演算子は関数適用より弱いので括弧が減って、Expr がずいぶん読みやすくなります。
evaluate ((C 10 `Add` ((C 8 `Div` C 2) `Mul` C 7)) `Sub` C 4) → 34
二項演算子2
Haskell なんですから、本当の二項演算子を使いましょう。data の中では二項演算子の名前は ":" から始める必要があるそうです。
data Expr = C Int | Expr :+ Expr | Expr :- Expr | Expr :* Expr | Expr :/ Expr deriving Show evaluate :: Expr -> Int evaluate (C x) = x evaluate (e1 :+ e2) = evaluate e1 + evaluate e2 evaluate (e1 :- e2) = evaluate e1 - evaluate e2 evaluate (e1 :* e2) = evaluate e1 * evaluate e2 evaluate (e1 :/ e2) = evaluate e1 `div` evaluate e2
記号になるだけで、ずいぶん直感的になりますね。
evaluate ((C 10 :+ ((C 8 :/ C 2) :* C 7)) :- C 4) → 34
優先順位
二項演算子には、優先順位が付けられるはずです。という訳で、これが最終的なプログラムです。
data Expr = C Int | Expr :+ Expr | Expr :- Expr | Expr :* Expr | Expr :/ Expr deriving Show evaluate :: Expr -> Int evaluate (C x) = x evaluate (e1 :+ e2) = evaluate e1 + evaluate e2 evaluate (e1 :- e2) = evaluate e1 - evaluate e2 evaluate (e1 :* e2) = evaluate e1 * evaluate e2 evaluate (e1 :/ e2) = evaluate e1 `div` evaluate e2 infixl 6 :* , :/ infixl 5 :+ , :-
以下の実行例を見れば、感動を押さえきれないでしょう。。。
evaluate (C 10 :+ C 8 :/ C 2 :* C 7 :- C 4) → 34
おまけ
あなたの言語で、これできますか?
トラックバック - http://d.hatena.ne.jp/kazu-yamamoto/20090204/1233732939
リンク元
- 9 http://k.hatena.ne.jp/keywordblog/Haskell
- 9 http://reader.livedoor.com/reader/
- 9 http://www.sampou.org/cgi-bin/haskell.cgi?グローバル変数が欲しい理由?
- 8 http://iiyu.asablo.jp/blog/2008/08/07/3676783
- 8 http://practical-scheme.net/wiliki/rssmix.cgi
- 6 http://www.mew.org/~kazu/
- 4 http://ezsch.ezweb.ne.jp/search/?sr=0101&query=マクスウェルの悪魔
- 4 http://ezsch.ezweb.ne.jp/search/?sr=0101&query=文章の書き方
- 4 http://search.yahoo.co.jp/search?p=文章の書き方&ei=UTF-8&fr=bb_top_v2&x=wrt
- 3 http://a.hatena.ne.jp/nobsun/simple

