2012-03-02 114 views
5

我最近做了Waterloo CCC,我觉得Haskell是回答这些类型问题的完美语言。我仍然在学习它。尽管如此,我仍然为输入而苦苦挣扎。Haskell:按行读取文件

下面是我使用的是什么:

import IO 
import System.Environment 
import System.FilePath 

… 

main = do 
    name <- getProgName 
    args <- getArgs 
    input <- readFile $ 
     if not (null args) 
      then head args 
      else dropExtension name ++ ".in" 
    let (k:code:_) = lines input 
    putStrLn $ decode (read k) code 

正如你所看到的,我在命令行中指定的文件路径或j1.in例如读取,如果该计划被称为j1.hs和编译j1

我只对该文件的前两行感兴趣,所以我使用了模式匹配来获取这些行,并将它们绑定到kcode,在本例中。然后我读取k作为整数,并将它和代码字符串传递给我输出的decode函数。

我想知道readFile是否将整个文件加载到内存中,这会很糟糕。但是后来我开始思考,也许因为Haskell很懒,它只读过前两行,因为这是后面要求的。我对吗?

此外,如果该代码示例中有任何内容可能更好或更具惯用性,请告诉我。

回答

8

documentation for readFile说:

readFile函数读取文件并返回该文件的内容作为字符串。根据需要,该文件被延迟阅读,与getContents一样。

所以,它只会读取文件的前两行(缓冲意味着它可能会在后台读取更多内容)。但是,这是readFile的一个属性,并不是所有Haskell I/O函数都是一般的。

惰性I/O对于I/O较重的程序(例如web服务器)来说是一个坏主意,但对于那些不需要执行太多I/O的简单程序来说,它可以很好地工作。

+0

在教程'hSetBuffering stdin LineBuffering'中,我看到用于标准输入,因为输入一次只能输入一行;会有文件输入的等价物吗?如果有使用它,或者会被认为是不成熟的优化? – mk12 2012-03-02 01:24:15

+3

我不同意你的观点,即惰性I/O只有在微不足道时才有用。惰性I/O非常有用的功能(I/O大量设置)的一个例子是,默认情况下数据处理是就地存在的,这意味着对于大量数据非常有效。 – amindfv 2012-03-02 04:25:17

+0

@amindfv:他并没有说懒惰I/O对于大型项目而言是无用的,他说它是_bad_。他的意思是懒惰的IO通常会导致资源泄漏(这里文件从未关闭,因为它不会被读到最后),这在大型复杂的程序中很难纠正。更优先考虑流式传输的原则性解决方案(比如Iteratee或更新的Conduit),因为它们可以更好地控制流量。 – Jedai 2012-03-02 13:45:13

6

是的,readFile是懒惰的。如果你想明确说明,你可以使用:

import Control.Monad (replicateM) 
import System.IO 

readLines n f = withFile f ReadMode $ replicateM n . hGetLine 

-- in main 
    (k:code:_) <- readLines 2 filename 

这将确保文件尽快关闭。

但是你做这件事的方式很好。

3

readFile懒惰地读取文件,所以它不会将整个文件读入内存,除非您使用整个文件。它通常不会读取前两行,因为它读取的是块,但它只会根据需要读取尽可能多的块以查找第二个换行符。

2

Haskell的I/O通常不是懒惰的。然而,readFile函数特别是懒。

其他人也说过同样的话。我还没有看到任何人指出的是,在程序结束或垃圾收集器运行之前,您打开的文件不会关闭。这只是意味着OS文件句柄可能会保持打开时间超过必要的时间。在你的程序中,这可能没什么大不了的。但是在一个更复杂的项目中,它可能是。