无法理解此代码中Monad州如何获得状态

时间:2019-01-09 19:51:52

标签: haskell monads state-monad

我有点理解State Monad在顺序执行中传播新值的有用性。但是在以下代码中,我在理解addResult每次获得评估时如何以及在何处获得更新状态时遇到了麻烦。

fizzBuzz :: Integer -> String
fizzBuzz n
   | n `mod` 15 == 0 = "FizzBuzz"
   | n `mod` 5 == 0 = "Buzz"
   | n `mod` 3 == 0 = "Fizz"
   | otherwise = show n

fizzbuzzList :: [Integer] -> [String]
fizzbuzzList list = execState (mapM_ addResult list) []

addResult :: Integer -> State [String] ()
addResult n = do
    xs <- get
    let result = fizzBuzz n
    put (result : xs)

main :: IO ()
main = mapM_ putStrLn $ fizzbuzzList [1..100]

此代码评估为产生

  

1、2,嘶嘶声...

我只是想不通addResult产生的新值如何附加到先前产生的列表中。您能帮我了解一下mapM_ addResult list是怎么回事吗?

2 个答案:

答案 0 :(得分:3)

正如您已经正确观察到的那样,State monad用于通过一系列计算来对某些外部“状态”值进行线程化。但是,您问如何在列表addResult :: Integer -> State [String] ()的每个值上对函数list的多次调用中“保持”这种状态。诀窍是mapM_的定义。我们将从考虑更简单的功能mapM开始:

mapM f [] = return []
mapM f (x:xs) = do
    fx <- f x
    fxs <- mapM f xs
    return (fx : fxs)

如果我们用示例列表[x1,x2,x3,x4,...,xn]来“扩展”此递归定义,我们将观察到mapM f [x1,...,xn]的值将是另一种单子计算:

do
    fx1 <- f x1
    fx2 <- f x2
    -- etc.
    fxn <- f xn
    return [fx1,fx2,...,fxn]

因此,mapM基本上是通过按顺序运行将一堆单子计算“组合”为一个大的。这就解释了如何构建列表而不是生成许多较小的列表:get开头的addResult从上次运行(而不是从开始)获取状态,因为您正在同时运行所有计算,所以:

do
    fl0 <- addResult (list !! 0)
    fl1 <- addResult (list !! 1)
    -- etc. like before

(如果仔细阅读,您会注意到我一直在谈论mapM,但是您实际上使用过mapM_。它们完全一样,只是后者返回{{1 }}。

答案 1 :(得分:2)

可以认为

State定义为

data State s a = s -> (a, s)

这意味着

Integer -> State [String] ()

等同于

Integer -> [String] -> ((), [String])

因此addResult接受一个整数并返回一个 function ,该函数接受一个状态并返回一个包含新状态的元组。

粗略地说,

mapM_将一组这些功能链接在一起。使用常规映射将产生State个函数的列表,每个函数都期望一个状态并返回一个新状态。 mapM_采取了进一步的步骤,将每个State绑定到前一个。最终结果不是State值的列表,而是构成管道的单个State值。

execState然后在管道的一端提供初始状态,并从另一端返回最终状态。