Stateモナドの解説 後編
-
Upload
masahiro-honma -
Category
Technology
-
view
915 -
download
2
Transcript of Stateモナドの解説 後編
Stateモナドの解説後編
2009年6月1日 id:hiratara
はじめに
• Stateモナドの値までわかった
• 今回は (>>=) によって関数をStateモナドの世界に持って来る
(1) (>>=) の定義
自然な合成を考えてみる
State Int Int
Int
Int
State Int Int
f
関数fをこんな定義にする
...
(a+1, 2)(a+2, 3)
(a+s0, s0+1)
12
s0
State Int Int
Int
Int
State Int Int
a
∋
∋
f
例: f 10 は、状態が 2 なら 12 となるような値
...
(11, 2)(12, 3)
(10+s0, s0+1)
12
s0
f 10 =
このfに次のような値を渡すとどうなって欲しいか
...
(2, 1)(4, 2)
(s0 × 2, s0)
12
s0
値は状態に依存する状態は変化しない
このfに次のような値を渡すとどうなって欲しいか
...
(2, 1)(4, 2)
(s0 × 2, s0)
12
s0
値は状態に依存する状態は変化しない
...
(3, 2)(6, 3)
(s0 × 2 + s0, s0 + 1)
12
s0
例えば、状態1の時元の値は2
fによる値は2+1 = 3
となるべき。
(=<<) f
(>>=) の定義と合わせてみて見る
(State x) >>= f = State $ \s -> let (v,s') = x s in runState (f v) s'
State(s0の関数)を (v(s0), s(s0)) と紫で表記して計算してみると...
(s0 × 2, s0) >>= f = (s0 × 2 + s0, s0 + 1)
(v, s') = (s0 × 2, s0) s = (s × 2, s)
f v = f (s × 2) = (s × 2 + s0, s0 + 1)
(f v) s' = (s × 2 + s0, s0 + 1) s = (s × 2 + s, s + 1)
よって、予想と一致!
まとめると、こういう感じ。
State Int Int
Int
Int
State Int Int
f(=<<) f
∋
∋
(s0 × 2, s0)
(s0 × 2 + s0, s0 + 1)
>>=
(1)のまとめ
• 状態によらない値 から 状態による値
を返す 関数f
• (>>=) により、f を 状態による値 にも適応することができるようになった
(2) get と put
put と getの位置付け
• モナドの実装 ( return と (>>=) ) によって、Stateの世界は完成している
• put と get は、Stateの世界を便利に使うためのユーティリティ関数
get• 現在の状態を表す、Stateの世界の値
• 現在の状態は現在の状態による(当たり前)
• 現在の状態を参照しても、状態は変わらない
get• 現在の状態を表す、Stateの世界の値
• 現在の状態は現在の状態による(当たり前)
• 現在の状態を参照しても、状態は変わらない
よって、こんな値になる → (s0, s0)
get
よって、こんな値になる → (s0, s0)
これはgetの定義と一致している
get = State $ \s -> (s,s)
put s• 特定の状態への変更を表す
State値を返す関数
• 値は不要 ⇔現在の値によらず () でいい
• 状態は渡された s になる
put s• 特定の状態への変更を表す
State値を返す関数
• 値は不要 ⇔現在の値によらず () でいい
• 状態は渡された s になる
よって、こんな関数になる → put s = ( (), s)
put s
よって、こんな関数になる → put s = ( (), s)
これはputの定義と一致している
put s = State $ \_ -> ((),s)
Int
() State Int ()
State Int Intput
put s
よって、こんな関数になる → put s = ( (), s)
>>= で 状態による値もput可能
put s = State $ \_ -> ((),s)
Int
() State Int ()
State Int Intput
(=<<) put
(3) 実例
State 型の 値を返すカウンターを考える
元の状態が0だと、値は1、次の状態は1。元の状態が1だと、値は2、次の状態は2。元の状態が2だと、値は3、次の状態は3。...
こんな値となる
State 型の 値を返すカウンターを考える
...
(1, 1)(2, 2)
(s0 + 1, s0+1)
01
s0
count =
元の状態が0だと、値は1、次の状態は1。元の状態が1だと、値は2、次の状態は2。元の状態が2だと、値は3、次の状態は3。...
State Int Int
どんな値かわかったので、直接実装できる
...
(1, 1)(2, 2)
(s0 + 1, s0+1)
01
s0
count =
count :: State Int Intcount = State $ \s -> (s + 1, s + 1)
State Int Int
(s0 + 1, s0+1)
=
一方で、getとputを使うと直感的に書ける
countA :: State Int IntcountA = do n <- get put $ n + 1 return $ n + 1
図解するとこう。
countA :: State Int IntcountA = do n <- get put $ n + 1 return $ n + 1
Int State Int Int
() State () Int
State Int IntInt
put
return() -> n+1
get ∈
put と return は >>= で 引き上げられる
countA :: State Int IntcountA = do n <- get put $ n + 1 return $ n + 1
Int State Int Int
() State () Int
State Int IntInt
put
return . ( ¥() -> n+1 )
get ∈
>>=
>>=
計算してみると、直接実装したものと同じになる
countA :: State Int IntcountA = do n <- get put $ n + 1 return $ n + 1
State Int Int
State () Int
State Int Int
get = (s0, s0) ∈Int
()
Int
put
return . ( ¥() -> n+1 ) >>=
>>=
∈
∈
( (), s0 + 1)
( s0 + 1, s0 + 1)
まとめ• >>= は、状態に依存する値を、状態を考慮して計算するような変換となる
• putとgetは便利ツール
• do 記法と put と get で、直感的な定義ができるが、結局はState型の値を作っていることになる
備考• runStateとかevalStateの解説は端折ってます。ほんとはこう。
State Int Bool Int -> (Bool, Int)runState
Intを適用
(Bool, Int)
evalState
Int -> Bool
Intを適用
Bool
ここが(v(s0), s(s0))の実体