简单的`foldM`示例

时间:2014-08-27 01:09:42

标签: haskell

查看foldM

foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a

我尝试创建foldM的示例,只是将列表[1,2,3]的每个元素附加到自身。

根据我对foldM的初步(错误)理解,我期望[[1], [2], [3]]作为以下内容的输出:

ghci> let f = (\xs x -> [x] : [xs])

但是,我错了:

ghci> foldM f [] [1,2,3]
[[3],[2],[3],[1],[3],[2],[3],[]]

请解释这个例子中发生了什么。

1 个答案:

答案 0 :(得分:13)

在阅读文档并在GHCi中使用foldM稍微调整一下之后,我想我可以解释一下你的例子中发生了什么。让我们重新检查foldM的类型签名:

foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a

从这种类型的签名中,我们可以得出结论:foldM接受函数(a -> b -> m a)并将其应用于列表的每个元素([b])。第二个参数是在“第一次调用”中传递给函数的初始值。后续调用使用应用函数的结果值(从m a中“提取”)。

因此,当你这样做时:

ghci> let f = (\xs x -> [x] : [xs])
ghci> foldM f [] [1,2,3]
[[3],[2],[3],[1],[3],[2],[3],[]]

相当于:

ghci> ([] `f` 1) >>= (`f` 2) >>= (`f` 3)
ghci> [[3],[2],[3],[1],[3],[2],[3],[]]

如果我们将上面的行划分为以下子表达式,我们可以更清楚地看到发生了什么:

ghci> ([] `f` 1)
ghci> [[1],[]]
ghci> ([] `f` 1) >>= (`f` 2)
ghci> [[2],[1],[2],[]]
...

函数f将列表和值作为参数,创建单个列表(将值放在自己的列表中)并将其添加到列表列表中。最初,当我们有一个空列表时,结果很明显:[[1],[]](这是我们在类型签名中的"m a")。现在,正如我之前所说,为了再次调用f,必须从该结果中获取新的"a"值。这一次,我们调用f传递提取的值和提供的列表中的下一个值(即来自2的{​​{1}})。问题是,考虑到我们的[1,2,3]"m a",我们应该将哪个列表作为第一个参数传递给[[1],[]]f[1]?答案依赖于列表的[]运算符的行为,可以将其视为非确定性计算,将给定函数应用于给定列表中的每个元素并组合结果。对于示例中的此特定步骤,>>=将针对两个不同的第一个参数调用两次:ff [1] 2

我试图根据作者给出的例子回答这个问题,但是在这个特殊情况下用于显式f [] 2行为的monadic链可以用来推理任何Monad。