首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >无法在文件夹中执行I/O?

无法在文件夹中执行I/O?
EN

Stack Overflow用户
提问于 2011-05-17 04:49:09
回答 4查看 1.5K关注 0票数 17

我有一个将Strings映射到Stringss的Data.Map结构。无论出于什么原因,我都希望使用foldrWithKeykey: value格式打印地图内容,如下所示:

代码语言:javascript
复制
M.foldrWithKey (\k v b -> putStrLn (k++": "++v++"\n")) (return ()) data

但是,只有map的第一个元素出现在输出中(即使map有多个元素)。但是,当我尝试使用foldrWithKey构造一个列表并打印它时,所有的元素都显示出来了:

代码语言:javascript
复制
print (M.foldrWithKey (\k v b -> k:b) [] data)

那么,为什么其他元素在尝试执行I/O时没有出现呢?是foldr的工作方式,还是我遗漏了一些与lazy-io相关的微妙怪癖?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-05-17 05:07:03

这是因为右折叠的工作方式,是的。折叠函数是一个累加:在每个步骤中,给定一个元素(在本例中是键和值)和其余数据的累加结果,它将它们组合成一个结果。作为一个整体,文件夹递归地执行该操作,以汇总整个数据集。

在本例中,您将丢弃累加器函数的"result“输入--请注意,从未使用过b参数。请记住,IO a不仅仅是附加了一些额外垃圾的a类型的值,它实际表示的是生成a的计算,并且该计算只能通过将其与其他计算组合在一起作为main函数(或者,在GHCi中,是被计算的表达式)的最终值的一部分来运行。

通过丢弃累加值,其他计算永远不会成为最终结果的一部分,因此这些值永远不会打印出来。

从您提出这个问题的方式来看,我猜您仍然更喜欢命令式编程,而不是Haskell的函数式编程。显然,在一种命令式语言中,在某种意义上,打印实际上是在折叠过程中“发生”的,因此可以合理地假设累加值是无用的。如果有帮助,可以更多地将其视为一种元编程;您实际上不是在编写一个用于打印值的循环,而是在构造一个执行实际打印的命令式程序,通过丢弃累加值,您基本上丢弃了展开的循环中除第一行以外的所有行,从而严重滥用了类比。

无论如何,在这种情况下,您可能需要采取“打印数据的其余部分”操作,即b参数,并使用(>>)将其与putStrLn ...操作组合在一起,这是一个运算符,基本上意味着“执行第一个操作,忽略结果,执行第二个操作”。这是对命令式风格“循环中的打印语句”的非常直接的翻译。

此外,虽然我知道这完全不是重点,但我可能会避免以任何方式混合格式和打印。在我看来,将每个键/值对分别格式化为一个列表,然后对其执行mapM_ putStrLn操作似乎更整洁。

mapM_是一个高阶函数,它描述了您在这里所做的事情的本质;给定一个a类型的列表和一个将a转换为某种IO操作的函数,它会将该函数应用于每一项,并按顺序运行生成的操作列表。Haskell的类型Monad m => (a -> m b) -> [a] -> m ()乍一看似乎很神秘,但是关于mapM_的好处之一是,一旦你习惯于阅读类型签名,Haskell的类型不仅一目了然,而且几乎是自记录的,因为对于具有该类型的函数来说,只有一件明智的事情要做,那就是mapM_本身所做的事情。

票数 23
EN

Stack Overflow用户

发布于 2011-05-17 05:08:10

这是一个不使用I/O的更清晰的示例。

代码语言:javascript
复制
foldr (\x b -> x) 9 [8,7,6,5,4,3,2,1,0]

此表达式返回列表的头部8。列表的其余部分都去了哪里?那么,处理列表的其余部分的结果被传递到'b‘中,它没有被使用,所以列表的其余部分被简单地忽略。

同样的事情也发生在你的案例中。通过忽略累加器'b',您正在构造一个只使用map的一个元素的I/O操作。基本上,您已经说过,“要打印地图,请打印它的第一个键和值。”您应该说的是,“要打印一个地图,先打印它的第一个键和值,然后再打印地图的其余部分。”为此,您需要安排变量'b‘的内容在putStrLn调用运行后运行:

代码语言:javascript
复制
M.foldrWithKey (\k v b -> do {putStrLn (k ++ ": " ++ v ++ "\n"); b}) (return ()) d
票数 11
EN

Stack Overflow用户

发布于 2011-05-17 05:10:19

将IO和漂亮的打印混合在一起是有点糟糕的形式,所以将IO浮动出来如何:

代码语言:javascript
复制
> putStr $ foldrWithKey (\k v b -> b ++ k ++ ": "++v++"\n") [] m

现在,至于代码不能正常工作的原因,考虑一下您的fold正在构建什么:b参数中的一系列打印语句。但是,每次循环都会丢弃b

因此,请跟踪它:

代码语言:javascript
复制
> foldrWithKey (\k v b -> putStrLn (k++": "++v) >> b) (return ()) m   

教训,不要扔掉你的累加器。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6023237

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档