我只是对列表和单子(monad)完全感到困惑,所以也许我的问题不正确或非常幼稚。
我已经看到了使用 mapM_ func here 的方法:
mapM_ print [1, 2, 3, 4]
但我不确切知道它是如何工作的,并且想知道如何以这样的方式做到这一点:
x <- [1, 2, 3]
print x
或者,如果我理解正确:
[1, 2, 3] >>= print
我知道 [1, 2, 3] 的类型为
[a]
打印类型为 Show a => a -> IO ()
.我也知道使用 monad List 我们需要输入 List a
在左侧和 func 类型为 a -> List b
在右侧。我对吗?你能帮我解决这个问题吗?
更新 .感谢@MathematicalOrchid 解释 mapM_ 的工作原理。从我的角度来看,我想解释真正的问题不是在不同的行中打印任何结果,而是以 monad List 提供的方式执行一些 monadic 操作(因为现在我正在闲逛 OpenGL 的东西)。但我知道误解的根源在于混合单子(monad)。
UPD2 .谢谢大家的回答。我为这个模糊的问题道歉。我不完全知道我需要什么答案以及问题是什么。那是因为我不了解一些基础知识。所以现在很难选择“正确的答案”,因为每个答案都与我正在寻找的东西有点和平。我决定选择最接近我想要的(虽然现在不是最有用的)。
最佳答案
您似乎在这里混淆了几件事。 (特别是,列表形成了一个 monad,而 I/O 形成了一个不同的 monad。)我将尝试澄清这一点......
首先,print
函数接受任何可显示的内容并将其写入标准输出,然后是换行符。所以print [1, 2, 3]
工作得很好,但显然将所有内容都写在同一行。要在单独的行上写东西,我们需要单独调用 print
对于每个项目。到现在为止还挺好。map
function 将函数应用于列表的每个元素。所以map print [1, 2, 3]
将申请 print
到列表中的每个项目。但是,结果是 I/O 操作列表。这不是我们所追求的。我们想要执行这些操作,而不是列出它们。
方法是使用 >>
运算符,它将两个 I/O 操作链接在一起(前提是您对它们的结果不感兴趣 - 并且打印某些内容不会返回任何有趣的内容)。所以foldr (>>) (return ())
将获取您的 I/O 操作列表并将其转换为单个 I/O 操作。这个函数实际上已经定义好了;它被称为 sequence
.
但是,map
+ sequence
是一个如此常见的组合,它也已经被定义;它被称为 mapM_
. (还有 mapM
,没有下划线,如果你想保留结果。但是打印不会返回任何东西,所以没有必要。)
这就是为什么mapM_
作品。现在你问为什么其他几种方法都行不通......
x <- [1, 2, 3]
print x
这根本行不通。第一行在列表 monad 中。但第二行在 I/O monad 中。你不能那样做。 (你会得到一个相当莫名其妙的类型检查错误。)我应该指出这是 Haskell 所谓的“do-notation”,上面的片段需要
do
前面的关键字使其实际上是有效的语法:do
x <- [1, 2, 3]
print x
无论哪种方式,它仍然无法正常工作。它几乎做了
map print [1, 2, 3]
有,但不完全。 (正如我所说,它不会进行类型检查。)您还建议
[1, 2, 3] >>= print
,这与前面的代码片段相同。 (实际上,编译器将前者转换为后者。)出于同样的原因,原版不进行类型检查,而本版也不进行类型检查。这有点像尝试将数字添加到矩阵中。数字是可加的东西。矩阵是可添加的东西。但是您不能将一个添加到另一个,因为它们不一样。如果这是有道理的。
关于list - 在新行中打印列表元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12461871/