Haskell のプログラムは全て式の置き換えで理解できる。

最近、興味がHaskellに舞い戻ってきた。ちょっとした指標を計算するときなど、Haskell でやるのが一番手軽だからだ。たとえば、100万円を年2%の複利で20年借りるといくらになるかというのは、Hugsをつかうと、

Hugs> 100 * 1.02 ** 20
148.594739597835

で48万円の利子がつくことになるのがわかる。思いついたことをパッパと実行してみることができる。

また、ちょっと技巧的になるが、フィボナッチ数列の値を知りたい時も、

Hugs> take 20 fib where fib = 1:1:zipWith (+) fib (tail fib)
[1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]

のように一行で計算できてしまう。とにかく、手軽なのだ。

プログラムを作って計算した値がおかしい時も、プログラムが短いので、バグも取りやすい。ただ、ループを全て再帰的プログラムで表現しないといけなかったり、今までの手続き型のプログラムとは発想がちがうのでなかなか馴染めない部分もある。便利かもしれないが勉強が大変なのではないかと敬遠する人も多いだろう。

しかし、Haskellの教科書を読んでいて、結局のところHaskellのプログラムの動作は全て、変数を式で置き換えることで説明がつくのに気がついた。たとえば、次のような関数を定義したとしよう。

f x y = x + y

この関数に引数 x = 1, y = 2 を与えると, 3 という値が得られるが、

Hugs> f 1 2 where f x y = x + y
3

これは、関数 f x y の変数 x, y にそれぞれ x = 1 と y = 2 を代入した式を計算していることになる。

f 1 2 = 1 + 2 = 3

もうちょっと複雑な例を考えてみよう。 g x = x * x を f 1 2 に適用させた g ( f 1 2 ) の出力は次のようになるが、

Hugs> g (f 1 2) where f x y = x + y; g x = x * x
9

これも、

g (f 1 2) = g (1 + 2) = g 3 = 3 * 3 = 9

のように、次々に式を展開していって値が得られているのがわかる。実は、再帰的定義のようなループ処理のプログラムも、単なる式の展開で説明ができるのだが長くなるので次のエントリーで説明する。

取り付きにくいHaskellだが、動作は全て式の展開で説明できることが分かると、意外にシンプルで分かりやすいことに気づく。

追記

上の説明では変数を数値で置き換えて展開を説明した。しかし、 Haslell は 遅延評価を行うので、実際の値の計算の前に式の展開だけがおきる。

g (f x y) = g (x + y) = (x + y) (x + y)

そうしてこれに引数の値 1 2 が与えられたときに g (f 1 2) = 9 が計算される。普段はそれを意識する必要はないが、遅延評価を利用したプログラムの動作説明で「値が必要になるまで計算が行われない」という説明があるときは、式の展開だけが起こっているのだとイメージすると分かりやすい。

追記 その2

本文の実行例は Hugs のものなので、ghci のものと少し違う。ghci で試すときは次のようにする。

フィボナッチ数

Prelude> let fib = 0:1:zipWith (+) fib (tail fib)
Prelude> take 10 fib
[0,1,1,2,3,5,8,13,21,34]

f x y と g x

Prelude> :set +m
Prelude> let
Prelude| f x y = x + y
Prelude| g x = x * x
Prelude|
Prelude> g (f 1 2)
9
[PR]
by tnomura9 | 2010-10-07 17:08 | Haskell | Comments(0)
<< Haskellでは再帰的定義も... 日中首脳会談の舞台裏 >>