假设我想生成一个项目列表,同时跟踪某些状态。例如,生成[1..]
,同时跟踪目前生成的项目,或生成一个随机数列表,记录RNG状态。生成带状态列表的最佳方式(Haskell)
看来,这样做有两种方式:
1)生成国家单子,[State s a]
的列表,然后运行sequence
得到State s [a]
。然后用runState
得到[a]
。 2)以某种方式使用Monad变压器。
monad变形金刚方式的一个很好的例子是什么?哪一种比较习惯?每个人的优点和缺点是什么?
假设我想生成一个项目列表,同时跟踪某些状态。例如,生成[1..]
,同时跟踪目前生成的项目,或生成一个随机数列表,记录RNG状态。生成带状态列表的最佳方式(Haskell)
看来,这样做有两种方式:
1)生成国家单子,[State s a]
的列表,然后运行sequence
得到State s [a]
。然后用runState
得到[a]
。 2)以某种方式使用Monad变压器。
monad变形金刚方式的一个很好的例子是什么?哪一种比较习惯?每个人的优点和缺点是什么?
我最近不得不做很多这方面的工作,我发现[State s a]
和sequence
是最好的选择。我想不出用Monad变形金刚这么做的有用/简单的方法。即使列表是Monad,使用sequence
意味着我们不必担心Monad内部的Monad。
我不得不使用MaybeT
创建一个Maybe Rand
,但从来没有列表和状态。尽管我只在哈斯克尔工作了几个月,所以可能有其他人可以用更多的经验来回答他们。
但是 - 并不总是找到使用Monads的方法。有时我发现不使用Monad更容易,而是使用Data.List附带的一些高阶函数。
这里是弘扬与列表值的某些方面,不涉及国家单子(如输入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。你必须为scanl
,mapAccumL
和unfoldr
导入时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]
你的例子是相当正交StateT
和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
生成4张随机数,我们跑了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_
丢弃它。
所以它不是解决这个问题的不同方法的问题,而是你是否需要“更大的锤子”来混合不同monads的动作。
monad变换器只是一种类型,它将其他monad作为参数,并将其他monadic效果添加到原始monad中。你永远不需要碰变压器来做你想做的事情。如果你需要跟踪状态,你可以使用'State';使用monad变压器并不意味着你必须或不能使用'State'。已经有一个包裹,它精确地使用'State'来纯粹地包装产生随机数的概念。 – user2407038 2014-09-11 06:59:28
这正是管道和导管的用途。 – 2014-09-11 08:11:38