2017-08-20 261 views
3

getLine懒惰?是getLine懒惰?

说我对输入有很长的一行。这只是一个数字序列。我只需要总结3个第一个数字。请问getLine是否高效,只读取该行的第一部分,或者我是否必须创建自己的函数来读取懒线,才能逐个读取字符?

如果我总结整条线,我的执行效率会高吗? (是否有一个开销由于一个读取字符一个?)

import Control.Applicative 

main = do 
    line <- getLine' 
    print $ sum $ map read $ take 3 $ words line 

getLine' :: IO String 
getLine' = do 
    c <- getChar 
    if c == '\n' then return [] else (c:) <$> getLine' 
+0

我觉得'getLine'和''getLine''都是同样严格的。 IO操作不能懒惰地返回,除非通过利用一些'unsafe'函数 - 这被称为“惰性IO”,并且必须小心处理,因为实际的读取会由于懒惰而在后面开始,这可能会导致一些问题。惰性IO是(很)很难调试的。但是,您可以使用严格的自定义'get3Ints',它只能读取所需字符串的一部分。 – chi

+3

'getLine'必须严格才能正确,正如chi所说,你的'getLine''表现完全一样。如果'getLine'不是严格的,那么你稍后做的纯计算会通过实现懒惰输入中的更多字符而导致IO。当你考虑其他的IO也可能会发生时,这将是一场噩梦,也从标准输入读取:哪些字符去哪里将是非常难以弄清楚。 – amalloy

+1

参考[这个回答](https://codereview.stackexchange.com/a/120037/16551)看到更多关于'IO'和懒惰的信息。如果你想要一个懒惰的'getLine',它可能需要一个类似于'IO(ListT IO Char)'的类型,其中'data ListT m a = Nil |缺点(m(ListT m a))'(如果您以给定长度的块读取输入以提高效率,您也可以使用'ListT IO String'而不是'ListT IO Char'。 – gallais

回答

1

虽然getLine不懒惰,getContents是,它可以用诸如lineswords功能相结合。因此,下面的程序将只读取足够的标准输入的从第一线获取(最多)三个整数和打印他们的总和:

main :: IO() 
main = do contents <- getContents 
      let lns = lines contents 
       result = sum $ map read $ take 3 $ words $ head lns 
      print (result :: Integer) 

需要注意的是,如果你修改程序来访问后续行 - 为例如,如果添加:

putStrLn $ take 80 $ lns !! 1 

到程序打印第一80个字符的第二行的的底部,则该程序将要完成读取所述第一线(因此将挂起对之间的位程序的最后两行),然后处理第二个80个字符。换句话说,这个懒线阅读仅在只有需要读取第一行的第一位时才有用,如果这对您不明显 - Haskell没有任何魔术方法可以跳过其余部分第一条线到达第二条。

最后,请注意,对于上面的程序,如果第一行的整数少于三个,它只会对这些数字进行求和,而不会尝试读取第一行(我认为这是你自找的)。如果您实际上并不在意线路结尾,只是想总结文件中的前三个数字,无论它们是如何分成线路的,那么您可以直接将内容分解成如下这样的文字:

main = do contents <- getContents 
      let result = sum $ map read $ take 3 $ words contents 
      print (result :: Integer)