Stateモナドの解説 後編

30
Stateモナドの解説 後編 200961id:hiratara

Transcript of Stateモナドの解説 後編

Page 1: Stateモナドの解説 後編

Stateモナドの解説後編

2009年6月1日 id:hiratara

Page 2: Stateモナドの解説 後編

はじめに

• Stateモナドの値までわかった

• 今回は (>>=) によって関数をStateモナドの世界に持って来る

Page 3: Stateモナドの解説 後編

(1) (>>=) の定義

Page 4: Stateモナドの解説 後編

自然な合成を考えてみる

State Int Int

Int

Int

State Int Int

f

Page 5: Stateモナドの解説 後編

関数fをこんな定義にする

...

(a+1, 2)(a+2, 3)

(a+s0, s0+1)

12

s0

State Int Int

Int

Int

State Int Int

a

f

Page 6: Stateモナドの解説 後編

例: f 10 は、状態が 2 なら 12 となるような値

...

(11, 2)(12, 3)

(10+s0, s0+1)

12

s0

f 10 =

Page 7: Stateモナドの解説 後編

このfに次のような値を渡すとどうなって欲しいか

...

(2, 1)(4, 2)

(s0 × 2, s0)

12

s0

値は状態に依存する状態は変化しない

Page 8: Stateモナドの解説 後編

この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

Page 9: Stateモナドの解説 後編

(>>=) の定義と合わせてみて見る

(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)

よって、予想と一致!

Page 10: Stateモナドの解説 後編

まとめると、こういう感じ。

State Int Int

Int

Int

State Int Int

f(=<<) f

(s0 × 2, s0)

(s0 × 2 + s0, s0 + 1)

>>=

Page 11: Stateモナドの解説 後編

(1)のまとめ

• 状態によらない値 から 状態による値

を返す 関数f

• (>>=) により、f を 状態による値 にも適応することができるようになった

Page 12: Stateモナドの解説 後編

(2) get と put

Page 13: Stateモナドの解説 後編

put と getの位置付け

• モナドの実装 ( return と (>>=) ) によって、Stateの世界は完成している

• put と get は、Stateの世界を便利に使うためのユーティリティ関数

Page 14: Stateモナドの解説 後編

get• 現在の状態を表す、Stateの世界の値

• 現在の状態は現在の状態による(当たり前)

• 現在の状態を参照しても、状態は変わらない

Page 15: Stateモナドの解説 後編

get• 現在の状態を表す、Stateの世界の値

• 現在の状態は現在の状態による(当たり前)

• 現在の状態を参照しても、状態は変わらない

よって、こんな値になる → (s0, s0)

Page 16: Stateモナドの解説 後編

get

よって、こんな値になる → (s0, s0)

これはgetの定義と一致している

get = State $ \s -> (s,s)

Page 17: Stateモナドの解説 後編

put s• 特定の状態への変更を表す

State値を返す関数

• 値は不要 ⇔現在の値によらず () でいい

• 状態は渡された s になる

Page 18: Stateモナドの解説 後編

put s• 特定の状態への変更を表す

State値を返す関数

• 値は不要 ⇔現在の値によらず () でいい

• 状態は渡された s になる

よって、こんな関数になる → put s = ( (), s)

Page 19: Stateモナドの解説 後編

put s

よって、こんな関数になる → put s = ( (), s)

これはputの定義と一致している

put s = State $ \_ -> ((),s)

Int

() State Int ()

State Int Intput

Page 20: Stateモナドの解説 後編

put s

よって、こんな関数になる → put s = ( (), s)

>>= で 状態による値もput可能

put s = State $ \_ -> ((),s)

Int

() State Int ()

State Int Intput

(=<<) put

Page 21: Stateモナドの解説 後編

(3) 実例

Page 22: Stateモナドの解説 後編

State 型の 値を返すカウンターを考える

元の状態が0だと、値は1、次の状態は1。元の状態が1だと、値は2、次の状態は2。元の状態が2だと、値は3、次の状態は3。...

こんな値となる

Page 23: Stateモナドの解説 後編

State 型の 値を返すカウンターを考える

...

(1, 1)(2, 2)

(s0 + 1, s0+1)

01

s0

count =

元の状態が0だと、値は1、次の状態は1。元の状態が1だと、値は2、次の状態は2。元の状態が2だと、値は3、次の状態は3。...

State Int Int

Page 24: Stateモナドの解説 後編

どんな値かわかったので、直接実装できる

...

(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)

=

Page 25: Stateモナドの解説 後編

一方で、getとputを使うと直感的に書ける

countA :: State Int IntcountA = do n <- get put $ n + 1 return $ n + 1

Page 26: Stateモナドの解説 後編

図解するとこう。

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 ∈

Page 27: Stateモナドの解説 後編

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 ∈

>>=

>>=

Page 28: Stateモナドの解説 後編

計算してみると、直接実装したものと同じになる

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)

Page 29: Stateモナドの解説 後編

まとめ• >>= は、状態に依存する値を、状態を考慮して計算するような変換となる

• putとgetは便利ツール

• do 記法と put と get で、直感的な定義ができるが、結局はState型の値を作っていることになる

Page 30: Stateモナドの解説 後編

備考• runStateとかevalStateの解説は端折ってます。ほんとはこう。

State Int Bool Int -> (Bool, Int)runState

Intを適用

(Bool, Int)

evalState

Int -> Bool

Intを適用

Bool

ここが(v(s0), s(s0))の実体