Haskell 他太懒了
我有以下代码片段,我将其传递给Haskell 他太懒了,haskell,file-io,Haskell,File Io,我有以下代码片段,我将其传递给with file: text <- hGetContents hand let code = parseCode text return code 我得到了一个公正的,我应该得到的 如果我自己做了openFile和hClose,我也会遇到同样的问题。为什么会这样?我怎样才能彻底解决它 谢谢您可以使用 length text `seq` return code 作为最后一行。hGetContents使用惰性IO;它仅在强制执行更多字符串时读取文件,并且仅
with file
:
text <- hGetContents hand
let code = parseCode text
return code
我得到了一个公正的,我应该得到的
如果我自己做了openFile
和hClose
,我也会遇到同样的问题。为什么会这样?我怎样才能彻底解决它
谢谢您可以使用
length text `seq` return code
作为最后一行。
hGetContents
使用惰性IO;它仅在强制执行更多字符串时读取文件,并且仅在计算返回的整个字符串时关闭文件句柄。问题是,您将它封装在中的文件;相反,只需直接使用openFile
和hGetContents
(或者更简单地说,readFile
)。完全计算字符串后,文件仍将关闭。类似这样的操作应该可以做到这一点,通过预先强制整个字符串来确保文件被完全读取并立即关闭:
import Control.Exception (evaluate)
readCode :: FilePath -> IO Code
readCode fileName = do
text <- readFile fileName
evaluate (length text)
return (parseCode text)
导入控制。异常(评估)
readCode::文件路径->IO代码
readCode fileName=do
文本hGetContents
不是太懒,它只是需要与其他内容适当组合才能达到预期效果。如果将其重命名为操作所需的ExposeContentsToEvaluation,或者仅将其重命名为侦听,情况可能会更清楚
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
在文件执行任何操作之前打开和关闭文件<代码>好
在打开和关闭文件之间执行所有操作。您的第一个操作类似于bad
withFile
应该控制所有需要执行的操作,这取决于句柄
如果您正在处理字符串
、小文件等,则不需要严格的强制执行器,只要了解组合的工作原理即可。同样,在bad
中,在关闭文件之前,我所做的一切都是exposeContentsToEvaluation,这是操作所需的。在good
中,我编写了操作所需的exposeContentsToEvaluation
以及我想到的其余操作,然后关闭文件
Patrick提到的熟悉的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
不用说,ByteString和Text是值得了解的,但是考虑到评估的重组更好,因为即使使用它们,惰性变体通常也是您所需要的,并且它们涉及到掌握构图形式之间的相同区别。如果您正在处理此类IO不合适的(大量)案例之一,请查看枚举器
,管道
和公司,它们都很好。我想说,仅仅对于严格的hGetContents
,依赖严格的
是值得的;这正是这个包裹的目的!不要传播NIH综合征。System.IO.Strict中hGetContents
的定义是熟悉的hGetContents h=IO.hGetContents h>=\s->length s`seq`return s
;这是本书中最古老的技巧,不是来自strict-0.3
的新想法,在读取字符串时使用evaluate
是毫无意义的,因为evaluate
只计算WHNF,即第一个(:)
构造函数。但是,如果解析文件的结果取决于文件的全部内容,则可能适合使用它;我提到它是因为这里的其他地方提到了它。这些长度
黑客真的很讨厌。这难道不意味着对于大多数程序来说,你的处境比仅仅使用openFile“filename”ReadMose>=>hGetContents
然后再也不关闭句柄更糟糕吗?因为使用withFile时,您必须在回调中完成整个程序的IO,否则您可能会导致延迟读取,除非句柄保证在最新的可能点关闭,而不是在任何您希望强制关闭并报告错误的点(如果可能出现后续延迟读取),您可以显示在您自己使用hClose的地方编码?听起来好像是在需要输入之前关闭它。这只需要计算元素的数量,由于length永远不会计算元素,因此haskell实现很容易只计算到文件的stating,而使用该实现时,在尝试使用数据时仍然会出现IO错误(让lseq=liftA2 seq in(length.filter(='\0'))`lseq`return)
将更接近完成,但仍可能存在惰性字符串源,它们将对最后一个字节进行编码,其中(='\0')不需要读取编码最后一个字符的所有字节。
$ 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