我有一个Data.Map将Strings 映射到Stringss 的结构.无论出于何种原因,我想key: value使用以下格式打印地图内容foldrWithKey,如下所示:
M.foldrWithKey (\k v b -> putStrLn (k++": "++v++"\n")) (return ()) data
Run Code Online (Sandbox Code Playgroud)
但是,只有地图的第一个元素出现在输出中(即使地图有多个元素).但是当我尝试使用foldrWithKey然后打印它来构建列表时,所有元素都显示出来:
print (M.foldrWithKey (\k v b -> k:b) [] data)
Run Code Online (Sandbox Code Playgroud)
那么,为什么其他元素在尝试执行I/O时不会出现?它是折叠器的工作方式还是有一些我遗漏的微妙的懒惰相关的怪癖?
C. *_*ann 23
这是因为正确的折叠是如何工作的,是的.折叠函数是一个累加:在每一步,给定一个元素(在这种情况下,键和值)和其余数据的累积结果,它将它们组合成一个结果.折叠作为一个整体以递归方式来总结整个数据集.
在您的情况下,您将丢弃累加器函数的"结果"输入 - 请注意,b参数永远不会被使用.请记住,这IO a不仅仅是a附加了一些额外垃圾的类型值,它实际上代表的是一个计算产生一个a,并且该计算只能通过将其与其他计算结合起来作为最终值的一部分来运行.main函数(或在GHCi中,被评估的表达式).
通过丢弃累积值,其他计算永远不会成为最终结果的一部分,因此值永远不会打印出来.
从你提出问题的方式来看,我猜你在命令式编程方面比Haskell的功能风格更舒服.显然,在一种命令式语言中,在折叠过程中打印实际上是"发生"的某种有意义的意义,假设累积值是无用的是合理的.如果它有所帮助,可以将其视为一种元编程; 你实际上并没有编写一个循环来打印值,你正在构建一个执行实际打印的命令式程序,并且通过丢弃累积的值,你基本上丢弃了除了展开的循环的第一行以外的所有内容,滥用比喻严重.
无论如何,在这种情况下你可能想要的是采取"打印剩余的数据"动作,b参数,并将其与putStrLn ...动作结合使用(>>),操作员基本上意味着"执行第一个动作,忽略结果,执行第二个".这是命令式"循环中的print语句"的直接翻译.
此外,虽然我理解它完全不是这一点,但我可能会避免混合格式和打印方式无论如何.在我看来,将每个键/值对分别格式化为列表似乎更整洁,然后就是mapM_ putStrLn这样.
mapM_是一个高阶函数,描述了你在这里所做的事情的本质; 给定一个类型的列表a和一个将其a转换为某种IO行为的函数,它将函数应用于每个项目并按顺序运行生成的操作列表.mapM_一开始Monad m => (a -> m b) -> [a] -> m ()看起来很神秘的类型,但是关于Haskell的一个好处就是,一旦你习惯了阅读类型签名,它mapM_的类型不仅一眼就能理解,它几乎是自我记录的,因为只有一个明智的东西对于具有该类型的函数,这正是mapM_它本身的作用.
Hea*_*ink 11
以下是一个更清晰的示例,说明正在发生的事情,而不使用I/O.
foldr (\x b -> x) 9 [8,7,6,5,4,3,2,1,0]
Run Code Online (Sandbox Code Playgroud)
此表达式返回8列表的头部.列表的其余部分在哪里?好吧,处理列表其余部分的结果是在'b'中传递,没有使用,所以列表的其余部分被简单地忽略了.
在你的情况下也会发生同样的事情.通过忽略累加器'b',您构建的I/O操作仅使用映射的一个元素.基本上你已经说过,"打印地图,打印它的第一把钥匙和价值." 您应该说的是,"打印地图,打印第一个键和值,然后打印地图的其余部分." 为此,您需要安排变量'b'的内容在运行调用后putStrLn运行:
M.foldrWithKey (\k v b -> do {putStrLn (k ++ ": " ++ v ++ "\n"); b}) (return ()) d
Run Code Online (Sandbox Code Playgroud)
混合IO和相当漂亮的打印有点糟糕,所以如何将IO浮出:
> putStr $ foldrWithKey (\k v b -> b ++ k ++ ": "++v++"\n") [] m
Run Code Online (Sandbox Code Playgroud)
现在,至于你的代码无法正常工作的原因,想一想你的代码是什么:参数中的一系列print语句b.但是,你b每次都在循环中扔掉!
所以要跟踪它:
> foldrWithKey (\k v b -> putStrLn (k++": "++v) >> b) (return ()) m
Run Code Online (Sandbox Code Playgroud)
教训,不要扔掉你的蓄电池.