【问题标题】:Is getLine lazy?getLine 是懒惰的吗?
【发布时间】:2018-01-28 22:22:44
【问题描述】:

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'

【问题讨论】:

  • 我认为库 getLine 和您的 getLine' 都同样严格。 IO 操作不能延迟返回,除非利用一些 unsafe 函数——这被称为“延迟 IO”,必须小心处理,因为由于延迟实际读取将在稍后开始,这可能会导致一些问题。 Lazy IO 是出了名的难以调试。但是,您可以使用严格的自定义 get3Ints,它只读取您需要的部分字符串。
  • getLine 必须严格才能正确,正如 chi 所说,您的 getLine' 行为完全相同。如果getLine 是非严格的,那么您稍后执行的纯计算将通过从惰性输入中实现更多字符而导致 IO。当您考虑到其他 IO 也可能在进行时,这将是一场噩梦,同时还从标准输入读取:哪些字符去哪里将非常难以弄清楚。
  • 参见。 this answer 了解更多关于 IO 和懒惰的信息。如果你想要一个懒惰的getLine,它可能需要一个类似于IO (ListT IO Char) 的类型,其中data ListT m a = Nil | Cons a (m (ListT m a))(如果你以给定的块读取输入,你也可以使用ListT IO String 而不是ListT IO Char长度更有效)。
  • 到底什么是反对票?

标签: haskell io lazy-evaluation processing-efficiency


【解决方案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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-02
    • 2011-03-24
    • 2019-04-07
    相关资源
    最近更新 更多