Yet Another Haskell Tutorial 25

9.3 A Simple State Monad

この節では、最初に状態を取り扱う Haskell のプログラムを作成している。状態というと、手続き型のプログラムでは変数のことと考えると良い。計算のいろんな段階で、途中の値を変数に保存しておくのは手続き型のプログラムでは常套手段だ。たとえば、1から10までの和を求める次のプログラムは、総和の計算の途中経過の値を total という変数に格納する。この total の値が状態を表しているといえる。

irb(main):001:0> begin
irb(main):002:1* total = 0
irb(main):003:1> for i in 1..10
irb(main):004:2>   total += i
irb(main):005:2> end
irb(main):006:1> puts "total = #{total}"
irb(main):007:1> end
total = 55

しかし、Haskell では変数の値を変えるような破壊的代入は許されていないので、Haskell で状態を表現するのには少し工夫がいる。

通常上の例のような計算の場合 Haskell では再帰関数を使う。

Prelude> :set +m
Prelude> let
Prelude| mysum [] = 0
Prelude| mysum (x:xs) = x + mysum xs
Prelude|
Prelude> mysum [1..10]
55

しかし、ここには total のような途中経過を納める変数(状態)はない。ところが、同じ再帰関数を末尾再帰でプログラムすると、再帰関数の引数として状態 total を表現できる。

Prelude> let
Prelude| stateSum total [] = total
Prelude| stateSum total (x:xs) = stateSum (total + x) xs
Prelude|
Prelude> stateSum 0 [1..10]
55

これだけでは、有り難みがわからないかもしれないので、上のプログラムを途中経過と一緒にリストにして表示するように変更してみる。

Prelude> let
Prelude| stateSum2 state [] = []
Prelude| stateSum2 state (x:xs) = (x, state + x) : stateSum2 (state + x) xs
Prelude|
Prelude> stateSum2 0 [1..10]
[(1,1),(2,3),(3,6),(4,10),(5,15),(6,21),(7,28),(8,36),(9,45),(10,55)]

このように、Haskell で状態を扱うプログラムを記述するときは末尾再帰の場合のように、引数の値として状態を渡すことが多い。

状態を取り扱うプログラムの例として、本文の例がわかりやすくなるようにもう一つ例を挙げてみる。

map 関数は map (*2) [1..10] のように、リストの要素のそれぞれに第1引数の関数を適用させて新しいリストを作る。

Prelude> map (*2) [1..10]
[2,4,6,8,10,12,14,16,18,20]

このプログラムでは状態は取り扱わない。それぞれの要素を計算するのに前の要素の計算結果を必要とはしないからだ。map 関数は自前で定義することもできる。

Prelude> let
Prelude| mymap f [] = []
Prelude| mymap f (x:xs) = f x : mymap f xs
Prelude|
Prelude> mymap (*2) [1..10]
[2,4,6,8,10,12,14,16,18,20]

しかし、リスト要素の総和を計算するプログラムのような場合、それ以前の要素の総和の情報が計算に必要になる。つぎの stateMap 関数は計算に state という状態を必要とする。

Prelude> let
Prelude| stateMap f state [] = state
Prelude| stateMap f state (x:xs) = stateMap f (f state x) xs
Prelude|
Prelude> stateMap (+) 0 [1..10]
55

このように、状態を取り扱った関数のプログラムは複雑になりやすい。YAHT のこのセクションでは、状態を取り扱うプログラムをモナドを利用したプログラムにすることで、非常に簡潔にすることができることを示している。

Yet Another Haskell Tutorial 26 へ続く ...
[PR]
by tnomura9 | 2013-03-09 14:30 | Haskell | Comments(0)
<< Yet Another Ha... Yet Another Has... >>