这两个非常相似的Haskell状态计算有什么区别?

3

我正在尝试学习Haskell State monad。因此,我编写了一个使用State monad生成随机数列表的函数。

这是第一个版本:

rnds :: Int -> [Int]
rnds n = evalState (help (mkStdGen 007)) []
  where help prng = do s <- get
            let (a, nprng) = randomR (1,6) prng                                 
            put (a:s)
            if length s == n then (return s)
                             else (help nprng)

这是第二个版本。
rnds1 :: Int -> [Int]
rnds1 n = evalState (help (mkStdGen 007)) []
  where help prng = do s <- get
            let (a, nprng) = randomR (1,6) prng                                 
            put (a:s)
            ns <- get
            if length ns == n then (return ns)
                              else (help nprng)

对于相同的参数,它们都会给出相同的输出。但是在第一个版本中,为了检查列表(也就是状态)的长度,我引用了列表s。但是,在执行put (a:s)之前,我获取了s。因此,当我检查长度时,我假设它会给出在执行put (a:s)之前's'的长度。但是似乎并不是这样,因为如果两个版本都给出相同的参数,则第一个版本的输出与第二个版本的输出相同。
对于我来说,第二个版本更容易理解。在检查列表ns的长度之前,我首先执行ns <- get以获取新的更新状态。
请问有人可以告诉我发生了什么事吗?我有一种强烈的感觉,我严重误解了Haskell工作方式或State monad本身的某些内容。
谢谢和问候。

展开do-notaion,State的>>=定义以及get和put的定义... http://hackage.haskell.org/packages/archive/mtl/1.1.0.2/doc/html/src/Control-Monad-State-Lazy.html#State - Don Stewart
2个回答

5
rnds 中,
put (a:s)
if length s == n then (return s)

你返回的是从 get 得到的列表,而不是 put 到状态中的列表,因此你产生了比 rnds1 多一个伪随机数(然后忽略它),但返回相同的列表。


4
正如Daniel所指出的那样,我认为这只是由于您使用的算法造成的巧合。如果您使用一个更简单的程序,就容易看到在从隐藏状态提取值后,这些值不会“神奇地”发生变异:
module Main where
import Control.Monad.State

test = evalState comp 1
  where
    comp = do
     x <- get
     put 2
     y <- get
     return (x,y)

main = do
   print test

这个程序会打印出(1,2),表明即使进行“put”操作,x仍然保留着“旧”的值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接