Hen*_*nes 3 file-io haskell lazy-evaluation
在这里,我又回来了(对我而言)我最新杰作的奇怪行为......
此代码应该读取文件,但它不会:
readCsvContents :: String -> IO ( String )
readCsvContents fileName = do
withFile fileName ReadMode (\handle -> do
contents <- hGetContents handle
return contents
)
main = do
contents <- readCsvContents "src\\EURUSD60.csv"
putStrLn ("Read " ++ show (length contents) ++ " Bytes input data.")
Run Code Online (Sandbox Code Playgroud)
结果是
Read 0 Bytes input data.
Run Code Online (Sandbox Code Playgroud)
现在我改变了第一个函数并添加了一个putStrLn
:
readCsvContents :: String -> IO ( String )
readCsvContents fileName = do
withFile fileName ReadMode (\handle -> do
contents <- hGetContents handle
putStrLn ("hGetContents gave " ++ show (length contents) ++ " Bytes of input data.")
return contents
)
Run Code Online (Sandbox Code Playgroud)
结果是
hGetContents gave 3479360 Bytes of input data.
Read 3479360 Bytes input data.
Run Code Online (Sandbox Code Playgroud)
WTF ??? 嗯,我知道,Haskell是懒惰的.但是我不知道我必须像这样踢它.
你是对的,这是一种痛苦.出于这个原因,避免使用旧的标准文件IO模块 - 除了简单地读取一个不会改变的整个文件,就像你做的那样; 这可以做得很好readFile
.
readCsvContents :: Filepath -> IO String
readCsvContents fileName = do
contents <- readFile fileName
return contents
Run Code Online (Sandbox Code Playgroud)
需要注意的是,由单子法律,这是完全一样的1作为
readCsvContents = readFile
Run Code Online (Sandbox Code Playgroud)
您尝试的问题是,当monad退出时withFile
,句柄无条件关闭,而不检查延迟评估contents
是否实际上强制文件读取.那当然太可怕了; 我自己也懒得用手柄.readFile
通过将句柄的关闭链接到原始结果thunk的垃圾收集来避免问题; 这也不是很好,但通常效果很好.
要正确使用文件IO,请检查管道或管道库.前者更注重性能,后者更注重优雅(但实际上,差异并不大).
1 你的第一次尝试与之相同readCsvContents fn = withFile fn ReadMode hGetContents
.