put 命令在使用 State monad 的典型函数中做了什么?
What does the put command do in a typical function using the State monad?
这是来自 https://wiki.haskell.org/All_About_Monads 的示例
这是一个使用 State
monad 通过一系列随机数生成命令来串接 StdGen
值的示例。
如果我理解最后一个 return
正确地做了什么,它应该只创建一个 new monad 并以 x
作为值。但是 put g'
到底做了什么?为什么 g'
实际上不会丢失?
getAny :: (Random a) => State StdGen a
getAny = do g <- get
(x,g') <- return $ random g
put g'
return x
我觉得你被
搞糊涂了
(x, g') <- return $ random g
这确实创建了一个新的 monadic 动作 State StdGen (a, StdGen)
,执行它以提取其结果 (a, StdGen)
。
混淆是有充分理由的,因为代码实际上等同于
let (x, g') = random g
其中没有构建 monadic 操作,导致代码更直接。
这种转换在任何 monad 中都是正确的,而不仅仅是 State
一个。
无论如何,技术部分:(x, g') <- return $ random g
片段表示
(x, g') <- State (\g'' -> (random g, g''))
我们可以看到 monadic 动作采用当前状态 g''
(与 g 具有相同的值),然后在返回时不修改它((..., g'')
部分)在它旁边生成值 random g
((random g, ...)
部分)。
这有点傻,因为我们甚至不需要 read g''
因为我们正在使用 random g
!
所以,我们正在使用
do g <- State (\g'' -> (g'', g''))
(x, g') <- State (\g'' -> (random g, g''))
...
何时我们可以使用
do (x, g') <- State (\g'' -> (random g'', g''))
...
在库中调用
do (x, g') <- gets random
...
好的,混淆似乎在 do put g' ; return x
。
这被脱糖成绑定符号如下
{ definitions }
put g' = State $ \s -> ((), g')
return x = State $ \s -> (x , s )
do put g ; return x
= { definitions, desugaring }
(State $ \s -> ((), g'))
>>=
(\_ -> State $ \s -> (x , s ))
= { definition of >>= }
State $ \s -> let (v,s') = (\s -> ((), g')) s
in runState ((\_ -> State $ \s -> (x , s )) v) s'
= { beta-reduction (application) }
State $ \s -> let (v,s') = ((), g')
in runState (State $ \s -> (x , s )) s'
= { beta-reduction (let) }
State $ \s -> runState (State $ \s -> (x , s )) g'
= { runState (State y) = y }
State $ \s -> (\s -> (x , s )) g'
= { beta-reduction }
State $ \s -> (x , g')
所以,do put g' ; return x
的作用是将状态修改为g'
(覆盖之前的s
),并产生x
作为最终值计算(以及 g'
)。
假设我们的状态存储在一个文件中。这是 getAny
用 Javascript/Python-like 语言表达的内容:
function getAny() {
var g = readStateFromFile("some-path") // the get call
var xg = random(g) // returns an array with two elements
var x = xg[0]
var newg = xg[1]
writeStateToFile("some-path", newg) // same as the put function call
return x
}
这里 random(g)
必须 return 两个值,所以我有它 return 一个数组。
现在考虑在这个调用序列中发生了什么:
a = getAny() same as: do a <- getAny
b = getAny() b <- getAny
c = getAny() c <- getAny
对于 a = getAny()
:
- 从文件中读取状态
- 它被赋予
random
其中 return 两个值
- 第二个值写入文件
- 第一个值被 returned 并存储在变量
a
中
然后 b = getAny()
:
- 刚刚写入文件的状态被读回
- 它被馈送到
random()
产生一个值和新状态
- 新状态写入文件
- 新值被 return 编辑并存储在变量
b
中
等...
现在回答你的问题:
what does the put g' actually do?
它用新值更新状态。
Why wouldn't the g' actually be lost?
newg
只是一个局部变量,所以它的值会丢失,除非我们将它保存在某个地方。
这是来自 https://wiki.haskell.org/All_About_Monads 的示例
这是一个使用 State
monad 通过一系列随机数生成命令来串接 StdGen
值的示例。
如果我理解最后一个 return
正确地做了什么,它应该只创建一个 new monad 并以 x
作为值。但是 put g'
到底做了什么?为什么 g'
实际上不会丢失?
getAny :: (Random a) => State StdGen a
getAny = do g <- get
(x,g') <- return $ random g
put g'
return x
我觉得你被
搞糊涂了(x, g') <- return $ random g
这确实创建了一个新的 monadic 动作 State StdGen (a, StdGen)
,执行它以提取其结果 (a, StdGen)
。
混淆是有充分理由的,因为代码实际上等同于
let (x, g') = random g
其中没有构建 monadic 操作,导致代码更直接。
这种转换在任何 monad 中都是正确的,而不仅仅是 State
一个。
无论如何,技术部分:(x, g') <- return $ random g
片段表示
(x, g') <- State (\g'' -> (random g, g''))
我们可以看到 monadic 动作采用当前状态 g''
(与 g 具有相同的值),然后在返回时不修改它((..., g'')
部分)在它旁边生成值 random g
((random g, ...)
部分)。
这有点傻,因为我们甚至不需要 read g''
因为我们正在使用 random g
!
所以,我们正在使用
do g <- State (\g'' -> (g'', g''))
(x, g') <- State (\g'' -> (random g, g''))
...
何时我们可以使用
do (x, g') <- State (\g'' -> (random g'', g''))
...
在库中调用
do (x, g') <- gets random
...
好的,混淆似乎在 do put g' ; return x
。
这被脱糖成绑定符号如下
{ definitions }
put g' = State $ \s -> ((), g')
return x = State $ \s -> (x , s )
do put g ; return x
= { definitions, desugaring }
(State $ \s -> ((), g'))
>>=
(\_ -> State $ \s -> (x , s ))
= { definition of >>= }
State $ \s -> let (v,s') = (\s -> ((), g')) s
in runState ((\_ -> State $ \s -> (x , s )) v) s'
= { beta-reduction (application) }
State $ \s -> let (v,s') = ((), g')
in runState (State $ \s -> (x , s )) s'
= { beta-reduction (let) }
State $ \s -> runState (State $ \s -> (x , s )) g'
= { runState (State y) = y }
State $ \s -> (\s -> (x , s )) g'
= { beta-reduction }
State $ \s -> (x , g')
所以,do put g' ; return x
的作用是将状态修改为g'
(覆盖之前的s
),并产生x
作为最终值计算(以及 g'
)。
假设我们的状态存储在一个文件中。这是 getAny
用 Javascript/Python-like 语言表达的内容:
function getAny() {
var g = readStateFromFile("some-path") // the get call
var xg = random(g) // returns an array with two elements
var x = xg[0]
var newg = xg[1]
writeStateToFile("some-path", newg) // same as the put function call
return x
}
这里 random(g)
必须 return 两个值,所以我有它 return 一个数组。
现在考虑在这个调用序列中发生了什么:
a = getAny() same as: do a <- getAny
b = getAny() b <- getAny
c = getAny() c <- getAny
对于 a = getAny()
:
- 从文件中读取状态
- 它被赋予
random
其中 return 两个值 - 第二个值写入文件
- 第一个值被 returned 并存储在变量
a
中
然后 b = getAny()
:
- 刚刚写入文件的状态被读回
- 它被馈送到
random()
产生一个值和新状态 - 新状态写入文件
- 新值被 return 编辑并存储在变量
b
中
等...
现在回答你的问题:
what does the put g' actually do?
它用新值更新状态。
Why wouldn't the g' actually be lost?
newg
只是一个局部变量,所以它的值会丢失,除非我们将它保存在某个地方。