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
粗略地说,bad
在文件执行任何操作之前打开和关闭文件;good
在打开和关闭文件之间做所有事情。你的第一个动作类似于bad
. withFile
应该管理您想要完成的所有操作,这取决于句柄。
如果您正在处理 .、小文件等,则不需要严格执行String
器,只需了解组合的工作原理即可。同样,在bad
关闭文件之前我所做的一切都是exposeContentsToEvaluationAsNeededForTheRestOfTheAction
. 在我构思的其余动作中进行good
撰写,然后关闭文件。exposeContentsToEvaluationAsNeededForTheRestOfTheAction
Patrick提到的熟悉的length
+技巧,或+值得了解;你的第二个动作是一个变体。但是重组更好,除非惰性 IO 不适合您的情况。seq
length
evaluate
putStrLn txt
$ 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
不言而喻,ByteString 和 Text 值得了解,但考虑到评估的重组更好,因为即使有了它们,你也经常需要 Lazy 变体,然后它们涉及到掌握组合形式之间的相同区别。如果您正在处理此类 IO 不合适的(大量)类案例之一,请查看enumerator
,conduit
和 co.,一切都很棒。