top page > computer > haskell > web_lecture > for_programmer > nsc_calc.html
更新日:
文責: 重城良国

Nano Scheme: 四則演算

(工事中 30%)

はじめに

四則演算のうち加算、減算、乗算を実装する。本格的なSchemeではこれらは3引数以上でも使えるリストに対する演算となっている。ここでは2項演算のみとする。

演算を保持する値

Environment.hs

演算を保持する値をValue型に追加する。

data Value
= Symbol Symbol
| Int Integer
| List [Value]
| Subr ([Value] -> Env -> Maybe (Value, Env))

表示する。

showValue (List vs) = ...
showValue (Subr _) = "#<subr>"

初期環境

Primitive.hs

+演算子の動作を関数addとして定義する。

add ::[Value] -> Env -> Maybe (Value, Env)
add [Int m, Int n] e = Just (Int $ m + n, e)
add _ _ = Nothing

引数が2個で整数値であればそれらを足した整数値を返す。環境は変更しない。そうでなければNothing値を返す。初期環境に+演算子を追加する。

env0 = fromList [
("hoge", Int 12345),
("+", Subr add)
]

ここまでを試してみる

変数+に足し算という手続きを定義した。

echo 'nsc +' | runghc -Wall nsc.hs
#<subr>

演算の適用の評価

Eval.hs

リスト値の評価は引数に手続きを適用するようにする。ifなどの「特殊形式」を実装することを考え、手続きの種類によって引数部分を評価するかどうかを決めるようにする。

関数eval

関数evalでリスト値では関数applyを呼び出すようにする。

eval (List (v : vs)) e = (\(f, e') -> apply f vs e') `mbind` eval v e

リストの先頭の要素を「評価」してその結果と残りの値のリスト、それと更新された環境を引数として関数applyを呼んでいる。

関数apply

apply :: Value -> [Value] -> Env -> Maybe (Value, Env)
apply (Subr s) vs e = (\(as, e') -> s as e') `mbind` evaluate vs e
apply _ _ _ = Nothing

適用するものがSubrなら残りの値をすべて評価してSubrの持つ関数を適用する。

足し算を試す

% echo 'nsc (+ 3 8)' | runghc -Wall nsc.hs
11
% echo 'nsc (+ (+ 3 8) (+ 5 7))' | runghc -Wall nsc.hs
34

引き算、かけ算の追加

引き算やかけ算は足し算とほとんど同じ枠組みを共有している。まずは関数addからその枠組みを抽出する。2つの整数値をとりNano Schemeの扱う値を返す関数から、Subrのなかみとして使える関数に変換する関数を考えていく。関数addのもともとの定義を再掲する。

add :: [Value] -> Env -> Maybe (Value, Env)
add [Int m, Int n] e = Just (Int $ m + n, e)
add _ _ = Nothing

演算子+の部分を抽象化すれば良い。

int2 :: (Integer -> Integer -> Value) -> [Value] -> Env -> Maybe (Value, Env)
int2 op [Int m, Int n] e = Just (m `op` n, e)
int2 _ _ _ = Nothing

こうすると関数addは以下のように書ける。

add op vs e = int2 (\m n -> Int $ m + n) op vs e

引数op, vs, eは消せる。

add = int2 (\m n -> Int $ m + n)

\m n -> Int $ m + nの部分も以下のようにポイントフリースタイルに直せる。

\m n -> Int $ m + n
\m n -> Int . (m +) $ n
\m -> Int . (m +)
\m -> (Int .) $ (+) m
\m -> (Int .) . (+) $ m
(Int .) . (+)

[定石] 1変数関数fと2変数関数gとの関数合成は(f .) . gのようにできる。

よってadd = int2 $ (Int .) . (+)となる。ここまでくればわざわざ関数addを定義する必要もない。関数addの定義は削除して以下のように直接書ける。

env0 = fromList [
("hoge", Int 12345),
("+", Subr . int2 $ (Int .) . (+))
]

変数-, *を追加する。

env0 = fromList [
...
("-", Subr . int2 $ (Int .) . (-)),
("*", Subr . int2 $ (Int .) . (*))
]

試してみる

コマンドライン

% echo 'nsc (* (+ 3 8) (- 5 2))' | runghc -Wall nsc.hs
33

Hubotで

Hubotを再起動して

nsc (* (+ 3 8) (- 5 2))

とすると

33

という答えが返る。

まとめ

加算、減算、乗算を実装した。

「Nano Scheme: リスト」へもどる 「Nano Scheme: 変数の定義」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics