gpt4 book ai didi

lazy-evaluation - hGetContents 是如何实现内存效率的?

转载 作者:行者123 更新时间:2023-12-04 02:43:16 28 4
gpt4 key购买 nike

我想将 Haskell 添加到我的工具箱中,所以我正在努力通过 Real World Haskell .

在输入和输出一章中,在 the section on hGetContents 中,我遇到了这个例子:

import System.IO
import Data.Char(toUpper)

main :: IO ()
main = do
inh <- openFile "input.txt" ReadMode
outh <- openFile "output.txt" WriteMode
inpStr <- hGetContents inh
let result = processData inpStr
hPutStr outh result
hClose inh
hClose outh

processData :: String -> String
processData = map toUpper

在这个代码示例之后,作者继续说:

Notice that hGetContents handled all of the reading for us. Also, take a look at processData. It's a pure function since it has no side effects and always returns the same result each time it is called. It has no need to know—and no way to tell—that its input is being read lazily from a file in this case. It can work perfectly well with a 20-character literal or a 500GB data dump on disk. (N.B. Emphasis is mine)

我的问题是:hGetContents 是怎么做到的?或其结果值实现此内存效率而无需 - 在本例中 - processData “能够分辨”,并且仍然保持纯代码(即 processData )带来的所有好处,特别是内存?

<- hGetContents inh返回一个字符串所以 inpStr绑定(bind)到 String 类型的值,这正是 processData 的类型接受。但是如果我正确理解 Real World Haskell 的作者,那么这个字符串与其他字符串不太一样,因为它没有完全加载到内存中(或者完全评估,如果存在未完全评估的字符串这样的东西...... .) 到调用 processData 时.

因此,另一种问我问题的方式是:如果 inpStr在调用 processData 时未完全评估或加载到内存中,那么如果对 processData 的内存调用,它如何用于查找存在,无需先全面评估 inpStr

是否有 String 类型的实例?每个人的行为都不同,但不能在这个抽象层次上区分?

最佳答案

hGetContents 返回的String 与任何其他 Haskell 字符串没有区别。一般来说,Haskell 数据不会被完全评估,除非程序员采取额外的步骤来确保它是(例如 seqdeepseq、bang patterns)。

字符串被定义为(本质上)

data List a = Nil | Cons a (List a) -- Nil === [], Cons === :
type String = List Char

这意味着一个字符串要么是空的,要么是一个字符(头)和另一个字符串(尾)。由于 laziness ,尾部在内存中可能不存在,甚至可能是无穷大。在处理 String 时,Haskell 程序通常会检查它是 Nil 还是 Cons,然后根据需要进行分支和继续。如果函数不需要计算尾部,它就不会。例如,这个函数只需要检查初始构造函数:

safeHead :: String -> Maybe Char
safeHead [] = Nothing
safeHead (x:_) = Just x

这是一个完全合法的字符串

allA's = repeat 'a' :: String

那是无限的。您可以明智地操作此字符串,但是如果您尝试打印所有字符串、计算长度或任何类型的无限遍历,您的程序将不会终止。但是您可以毫无问题地使用像 safeHead 这样的函数,甚至可以使用一些有限的初始子字符串。

但是,您认为发生了一些奇怪的事情的直觉是正确的。 hGetContents 是使用特殊函数 unsafeInterleaveIO 实现的,它本质上是一个编译器 Hook 到 IO 行为。越少说越好。

你是对的,如果不对参数进行完全评估,很难检查是否存在对函数的内存调用。但是,大多数编译器不执行此优化。问题在于,编译器很难确定何时值得内存调用,而且这样做很容易耗尽所有内存。还好有several memoizing libraries您可以在适当的时候使用它来添加内存。

关于lazy-evaluation - hGetContents 是如何实现内存效率的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19420450/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com