2

这是来自https://wiki.haskell.org/All_About_Monads 的示例 这是使用StatemonadStdGen通过一系列随机数生成命令来线程化值的示例。如果我理解最后一个return正确的做法,它应该只创建一个的monadx作为值。但那put g'实际上是做什么的呢?为什么g'实际上不会丢失?

getAny :: (Random a) => State StdGen a
getAny = do g <- get
            (x,g') <- return $ random g
            put g'
            return x
4

2 回答 2

3

假设我们的状态被存储在一个文件中。以下是getAny用类似 Javascript/Python 的语言表达的内容:

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)必须返回两个值,所以我让它返回一个数组。

现在考虑在这一系列调用期间发生了什么:

 a = getAny()       same as:    do a <- getAny
 b = getAny()                      b <- getAny
 c = getAny()                      c <- getAny

对于a = getAny()

  • 从文件中读取状态
  • 它被赋予random返回两个值
  • 第二个值写入文件
  • 返回第一个值并存储在变量中a

然后为b = getAny()

  • 刚刚写入文件的状态被读回
  • 它被用来random()产生一个价值和新的状态
  • 新状态被写入文件
  • 返回新值并存储在变量中b

ETC...

现在回答你的问题:

put g' 实际上是做什么的?

它使用新值更新状态。

为什么g'实际上不会丢失?

newg只是一个局部变量,所以除非我们将它保存在某个地方,否则它的值将会丢失。

于 2016-06-19T16:13:30.410 回答
1

我想你很困惑

(x, g') <- return $ random g

这确实创建了一个新的单子动作State StdGen (a, StdGen),执行该动作以提取其结果(a, StdGen)

产生混淆是有充分理由的,因为代码实际上等同于

let (x, g') = random g

没有构建单子动作,从而导致更直接的代码。这种转换在任何单子中都是正确的,而不仅仅是State一个。

无论如何,技术部分:(x, g') <- return $ random g片段意味着

(x, g') <- State (\g'' -> (random g, g''))

我们可以看到一元动作采用当前状态g''(与 g 具有相同的值),然后不修改它((..., g'')部分),同时返回生成的值random g(random g, ...)部分)。

这有点傻,因为我们甚至不需要阅读 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')产生。

于 2016-06-19T16:06:31.267 回答