无法理解 State Monad 如何在此代码中获取它的状态
Unable to understand how State Monad get it's state in this code
我有点理解 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, Fizz...
我只是想不通 addResult
产生的新值是如何附加到先前产生的列表中的。你能帮我理解一下 mapM_ addResult list
是怎么回事吗?
State
可以被认为是像
这样定义的
data State s a = s -> (a, s)
这意味着
Integer -> State [String] ()
等同于
Integer -> [String] -> ((), [String])
所以addResult
接受一个整数,returns一个函数接受一个状态,returns一个包含新状态的元组。
mapM_
,粗略地说,将一组这些函数链接在一起。使用常规映射会产生一个 State
函数列表,每个函数都需要一个状态并返回一个新状态。 mapM_
进一步将每个 State
绑定到前一个。最终结果不是 State
值的列表,而是形成管道的单个 State
值。
execState
然后在管道的一端提供初始状态,returns 从另一端提供最终状态。
正如您正确观察到的那样,State monad 用于通过一系列计算将一些外部 'state' 值串联起来。但是,您询问在对列表 list
的每个值多次调用您的函数 addResult :: Integer -> State [String] ()
时,这种状态是如何 'persisted' 的。诀窍是 mapM_
的定义。我们将从考虑更简单的函数 mapM
:
开始
mapM f [] = return []
mapM f (x:xs) = do
fx <- f x
fxs <- mapM f xs
return (fx : fxs)
如果我们在脑海中 'expand' 这个递归定义,例如,一个示例列表 [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
基本上 'sticks together' 一堆单子计算变成一个大的,通过 运行 按顺序将它们组合在一起。这解释了如何构建一个列表而不是生成许多较小的列表:addResult
开头的 get
从最后一个 运行[=34= 获取状态 ],而不是从一开始,因为您要 运行 将所有计算结合在一起,如下所示:
do
fl0 <- addResult (list !! 0)
fl1 <- addResult (list !! 1)
-- etc. like before
(如果你仔细阅读,你会发现我一直在谈论 mapM
,但你实际上使用了 mapM_
。它们完全相同,只是后者 returns ()
.)
我有点理解 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, Fizz...
我只是想不通 addResult
产生的新值是如何附加到先前产生的列表中的。你能帮我理解一下 mapM_ addResult list
是怎么回事吗?
State
可以被认为是像
data State s a = s -> (a, s)
这意味着
Integer -> State [String] ()
等同于
Integer -> [String] -> ((), [String])
所以addResult
接受一个整数,returns一个函数接受一个状态,returns一个包含新状态的元组。
mapM_
,粗略地说,将一组这些函数链接在一起。使用常规映射会产生一个 State
函数列表,每个函数都需要一个状态并返回一个新状态。 mapM_
进一步将每个 State
绑定到前一个。最终结果不是 State
值的列表,而是形成管道的单个 State
值。
execState
然后在管道的一端提供初始状态,returns 从另一端提供最终状态。
正如您正确观察到的那样,State monad 用于通过一系列计算将一些外部 'state' 值串联起来。但是,您询问在对列表 list
的每个值多次调用您的函数 addResult :: Integer -> State [String] ()
时,这种状态是如何 'persisted' 的。诀窍是 mapM_
的定义。我们将从考虑更简单的函数 mapM
:
mapM f [] = return []
mapM f (x:xs) = do
fx <- f x
fxs <- mapM f xs
return (fx : fxs)
如果我们在脑海中 'expand' 这个递归定义,例如,一个示例列表 [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
基本上 'sticks together' 一堆单子计算变成一个大的,通过 运行 按顺序将它们组合在一起。这解释了如何构建一个列表而不是生成许多较小的列表:addResult
开头的 get
从最后一个 运行[=34= 获取状态 ],而不是从一开始,因为您要 运行 将所有计算结合在一起,如下所示:
do
fl0 <- addResult (list !! 0)
fl1 <- addResult (list !! 1)
-- etc. like before
(如果你仔细阅读,你会发现我一直在谈论 mapM
,但你实际上使用了 mapM_
。它们完全相同,只是后者 returns ()
.)