hGetContents太懒了

Kar*_*elė 13 file-io haskell

我有以下代码片段,我传递给withFile:

text <- hGetContents hand 
let code = parseCode text
return code
Run Code Online (Sandbox Code Playgroud)

这里hand是一个有效的文件句柄,打开ReadMode并且parseCode是我自己的函数,它读取输入并返回一个Maybe.实际上,函数失败并返回Nothing.如果,而是我写:

text <- hGetContents hand 
putStrLn text
let code = parseCode text
return code
Run Code Online (Sandbox Code Playgroud)

我得到了一个Just,就像我应该的那样.

如果我openFilehClose我自己,我有同样的问题.为什么会这样?我怎样才能干净利落地解决它?

谢谢

app*_*ive 12

hGetContents不是太懒,它只需要与其他东西合适地组合以获得理想的效果.如果它被重命名exposeContentsToEvaluationAsNeededForTheRestOfTheAction或仅仅是情况,情况会更清楚listen.

withFile 打开文件,执行某些操作(或者根本不做任何操作 - 确切地说,无论如何都需要它),并关闭文件.

揭开"懒惰IO"的所有神秘面纱是不够的,但现在考虑一下这种包围的区别

 good file operation = withFile file ReadMode (hGetContents >=> operation >=> print)
 bad file operation = (withFile file ReadMode hGetContents) >>= operation >>= print

-- *Main> good "lazyio.hs" (return . length)
-- 503
-- *Main> bad "lazyio.hs" (return . length)
-- 0
Run Code Online (Sandbox Code Playgroud)

bad在它做任何事之前,粗略地放置,打开和关闭文件; good在打开和关闭文件之间做所有事情.你的第一个动作类似于bad.withFile应该控制你想要完成的所有动作,这取决于句柄.

如果您正在使用String,小文件等,您不需要严格执行者,只需了解组合的工作原理.再次,在bad关闭文件之前我所做的一切都是exposeContentsToEvaluationAsNeededForTheRestOfTheAction.在good我构思exposeContentsToEvaluationAsNeededForTheRestOfTheAction我想到的其余动作时,然后关闭文件.

熟悉length+ seq帕特里克,或提及招length+ evaluate是值得了解的; 你的第二个动作putStrLn txt是变种.但重组更好,除非懒惰IO对你的情况是错误的.

$ time ./bad
bad: Prelude.last: empty list  
                        -- no, lots of Chars there
real    0m0.087s

$ time ./good
'\n'                -- right
()
real    0m15.977s

$ time ./seqing 
Killed               -- hopeless, attempting to represent the file contents
    real    1m54.065s    -- in memory as a linked list, before finding out the last char
Run Code Online (Sandbox Code Playgroud)

不言而喻,ByteString和Text值得了解,但是考虑到评估的重组更好,因为即使使用它们,Lazy变体通常也是你需要的,然后他们就会在构图形式之间理解相同的区别.如果你正在处理这种IO不适合的(巨大的)类别的情况之一,那么看看enumerator,conduit并且co.,一切都很棒.


ehi*_*ird 9

hGetContents使用懒惰的IO; 它只会在您强制执行更多字符串时从文件中读取,并且只在评估它返回的整个字符串时才关闭文件句柄.问题是你把它封闭在里面withFile; 相反,只需使用openFilehGetContents直接(或更简单地说readFile).完全评估字符串后,文件仍将关闭.像这样的东西应该做的,以确保文件完全读取和立即通过强制整个字符串关闭:

import Control.Exception (evaluate)

readCode :: FilePath -> IO Code
readCode fileName = do
    text <- readFile fileName
    evaluate (length text)
    return (parseCode text)
Run Code Online (Sandbox Code Playgroud)

像这样的不直观的情况是人们现在倾向于避免懒惰IO的原因之一,但不幸的是你不能改变它的定义hGetContents.严格的包中hGetContents提供了严格的IO版本,但它可能不值得依赖于该功能的包.

如果你想避免在这里两次遍历字符串所产生的开销,那么你应该考虑使用一种比它更有效的类型String.该Text类型有严格的IO当量为多的String基于IO功能,一样ByteString(如果你处理的二进制数据,而不是Unicode文本).

  • 我会说它*值*依赖于`strict`只是为了严格的'hGetContents`; 这正是包的目的!不要传播NIH综合征. (3认同)
  • `System.IO.Strict`中`hGetContents`的定义是熟悉的`hGetContents h = IO.hGetContents h >> =\s - > length s \`seq \`return s`; 这是本书中最古老的技巧,而不是来自`strict-0.3`的新颖想法 (2认同)