8

我正在尝试在haskell 中进行一个小实验,想知道是否可以利用惰性来处理IO。我想编写一个函数,它接受一个字符串(一个字符列表)并懒惰地产生一个字符串。然后我希望能够从 IO 懒惰地向它提供字符,这样每个字符都会在可用时立即处理,并且在必要的字符可用时生成输出。但是,我不太确定是否/如何从 IO monad 中的输入生成惰性字符列表。

4

3 回答 3

15

Haskell 中的常规字符串 IO 是惰性的。所以你的例子应该开箱即用。

这是一个使用“interact”函数的示例,该函数将函数应用于惰性字符流:

interact :: (String -> String) -> IO ()

让我们从输入流中过滤掉字母'e',懒惰地(即在恒定空间中运行):

main = interact $ filter (/= 'e')

如果您愿意,也可以使用 getContents 和 putStr。他们都很懒惰。

运行它以过滤字典中的字母“e”:

$ ghc -O2 --make A.hs
$ ./A +RTS -s < /usr/share/dict/words
...
               2 MB total memory in use (0 MB lost due to fragmentation)
...

所以我们看到它以恒定的 2M 足迹运行。

于 2010-02-18T17:18:38.537 回答
8

做惰性 IO 的最简单方法涉及诸如interactreadFilehGetContents之类的函数,正如dons所说的那样;在Real World Haskell一书中对这些内容进行了更深入的讨论,您可能会发现它们很有用。如果我没记错的话,所有这些功能最终都是使用unsafeInterleaveIO那个ehemient提到的来实现的,所以如果你愿意,你也可以这样构建你自己的函数。

另一方面,注意这unsafeInterleaveIO正是它在锡上所说的内容可能是明智的:unsafe IO。使用它——或基于它的函数——破坏了纯度和引用透明度。这允许明显的纯函数(即不返回IO动作)在评估时影响外部世界,从相同的参数产生不同的结果,以及所有其他不愉快的事情。在实践中,大多数明智的使用方式unsafeInterleaveIO不会导致问题,简单的错误通常会导致明显且易于诊断的错误,但您已经失去了一些很好的保证。

当然,还有其他选择;你可以在 Hackage 上找到各种库,这些库提供了受限的、更安全的惰性 IO概念上不同的方法。但是,鉴于在实际使用中很少出现问题,我认为大多数人倾向于坚持使用内置的、技术上不安全的功能。

于 2010-02-18T18:11:26.010 回答
3
unsafeInterleaveIO :: IO a -> IO a

unsafeInterleaveIOallosIO计算被延迟延迟。当传递一个 type 的值时IO aIO只有在需要 的值时才会执行a。这用于实现延迟文件读取,请参阅System.IO.hGetContents.

例如,main = getContents >>= return . map Data.Char.toUpper >>= putStr是懒惰的;当您将字符输入标准输入时,您将在标准输出上获得字符。

(这与写作相同main = interact $ map Data.Char.toUpper,如唐斯的回答。)

于 2010-02-18T17:19:30.853 回答