生成状态列表的最佳方法(Haskell)

时间:2014-09-11 06:29:42

标签: haskell monads monad-transformers state-monad

假设我想生成一个项目列表,同时跟踪一些状态。例如,生成[1..],同时跟踪到目前为止生成的项目,或生成随机数列表,跟踪RNG状态。

似乎有两种方法可以做到这一点:

1)生成State Monads列表[State s a],然后运行sequence以获得State s [a]。然后使用runState获取[a]

2)以某种方式使用Monad变形金刚。

monad变形金刚方式的一个很好的例子是什么?哪个更惯用?每个人的利弊是什么?

2 个答案:

答案 0 :(得分:4)

我最近必须做很多事情,我发现[State s a]sequence是最好的选择。我无法想到使用Monad变形金刚这样做的有用/简单方法。即使list是Monad,使用sequence也意味着我们不必担心Monad中的Monad。

我必须使用MaybeT来创建Maybe Rand,但从不使用列表和状态。虽然我只做了几个月的Haskell,所以可能还有其他人可以回答他们背后的更多经验。

然而 - 并不总是想找到一种方法来使用Monads。有时我发现不使用Monad更容易,而是使用Data.List附带的一些高阶函数。

以下是一些使用不涉及Monad状态(作为GHCi输入)的列表来转发值的方法:

> :t scanl
scanl :: (a -> b -> a) -> a -> [b] -> [a]
> scanl (+) 0 [1,2,3,4,5]
[0,1,3,6,10,15]

> :t mapAccumL
mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
> mapAccumL (\acc x -> (x+acc,x*2)) 0 [1,2,3,4,5]
(15,[2,4,6,8,10])

> :t unfoldr
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
> take 20 . unfoldr (\g -> Just $ randomR (0,10) g) $ mkStdGen 444
[2,3,8,0,7,5,2,10,10,5,10,2,4,2,8,9,1,1,5,10]

NB。您必须导入scanlmapAccumLunfoldr

的Data.List

使用随机数时,有时可以更轻松地生成我们需要的随机数列表,而不是创建[Rand StdGen Int]列表。例如,此函数使用applicatives生成随机大小的随机数列表:

> :t runRand
runRand :: RandomGen g => Rand g a -> g -> (a, g)
> fst . flip runRand (mkStdGen 12345) $ take <$> getRandomR (10,15) <*> getRandomRs (10,20) :: [Int]
[17,16,16,19,16,17,15,12,10,11,11,10,17,12,13]

答案 1 :(得分:4)

您的示例与StateTState之间的差异非常正交。

如果我们使用State生成四个随机数,就像这样

import System.Random
import Control.Monad.State

rs :: State StdGen Int
rs = do
  (r,g) <- random `fmap` get
  put g
  return (r :: Int)

main = getStdGen >>= \g -> mapM_ print . flip evalState g . replicateM 4 $ rs

我们四次运行State操作,将结果收集到列表中,最后打印列表中的每个数字。但是,如果由于某种原因,我们需要在内部执行IO 而不是在收集结果之后呢?这就是变压器变得相关的地方

import System.Random
import Control.Monad.State

rs' :: StateT StdGen IO Int
rs' = do
  (r,g) <- random `fmap` get
  put g
  liftIO $ print r
  return (r :: Int)

main = getStdGen >>= \g -> flip evalStateT g . replicateM_ 4 $ rs'

请注意,虽然仍然可以访问最终结果,但我使用replicateM_将其丢弃。

所以问题不在于解决这个问题的不同方法,而在于你是否需要更大的锤子&#34;为了混合不同单子的行为。