这是因为正确的折叠是如何工作的,是的。折叠函数是一种累加:在每一步,给定一个元素(在本例中为键和值)和其余数据的累加结果,它将它们组合成一个结果。折叠作为一个整体递归地总结整个数据集。
在您的情况下,您将丢弃累加器函数的“结果”输入——请注意,该b
参数从未使用过。请记住,这IO a
不仅仅是一个a
附加了一些额外垃圾的类型值,它实际上表示的是产生一个的计算a
,并且该计算只能通过将其与其他计算结合作为最终值的一部分来运行main
函数(或者,在 GHCi 中,被评估的表达式)。
通过丢弃累积值,其他计算永远不会成为最终结果的一部分,因此这些值永远不会被打印出来。
从您提出问题的方式来看,我猜您在命令式编程中仍然比 Haskell 的函数式编程更舒服。显然,在某种有意义的意义上,在折叠过程中打印实际上“发生”的命令式语言中,假设累积值是无用的是合理的。如果有帮助,请将此更多地视为一种元编程;您实际上并没有编写循环来打印值,而是构建了一个执行实际打印的命令式程序,并且通过丢弃累积的值,您基本上丢弃了展开循环的第一行以外的所有内容,以滥用比喻不好。
无论如何,在这种情况下,您可能想要的是采取“打印其余数据”操作,b
参数,并将其与putStrLn ...
操作结合使用(>>)
,该操作符基本上意味着“执行第一个操作,忽略结果,执行第二个”。这是对命令式“循环打印语句”的非常直接的翻译。
此外,虽然我理解这完全是题外话,但我可能会避免以这种方式混合格式和打印。在我看来,将每个键/值对分别格式化为一个列表似乎更整洁,然后再过mapM_ putStrLn
一遍。
mapM_
是一个高阶函数,描述了你在这里所做的事情的本质;给定某种类型的列表a
和将 ana
转换为某种IO
动作的函数,它将该函数应用于每个项目并按顺序运行生成的动作列表。mapM_
有Monad m => (a -> m b) -> [a] -> m ()
一个起初看起来很神秘的类型,但是关于 Haskell 的好处之一是,一旦你习惯阅读类型签名,mapM_
的类型不仅一眼就能理解,它几乎是自我记录的,因为只有一个明智的事情对于具有该类型的函数来说,这正是mapM_
它本身所做的。