2017-08-20 158 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)