繁体   English   中英

了解Haskell的懒惰

[英]Understanding Haskell's Laziness

我正在阅读: Haskell交互功能

所以我尝试了

interact (unlines . map (show . length) . lines)

它按我的预期工作。 我输入一些内容,然后按Enter键,然后在提示处得到打印的长度。

因此,我想尝试使其重复输入的内容,因此我尝试了

interact (unlines . map id . lines)

但是现在它会重复我键入的每个字符。为什么? 我认为,诀窍是在lines之后unlines -但它显然不是。 lines "a"产生["a"] ,那么当我开始输入输入时,第一个函数又如何呢?它不只是立即将“ 1”作为输出? 对于“发现字符串的长度并不像这样-在生成任何输出之前必须知道整个字符串”,我显然有误解。

这与延迟评估有关。 我将尝试以尽可能直观的方式进行解释。

当您编写interact (unlines . map (show . length) . lines) ,每次输入一个字符时,我们实际上不知道下一个输出字符是什么,直到您按Enter键。 因此,您将获得预期的行为。

但是,在interact (unlines . map id . lines) = interact id每个点interact (unlines . map id . lines) = interact id ,每次输入一个字符时,都可以确保该字符包含在输出中。 因此,如果输入一个字符,该字符也会立即输出。

这是“懒惰”一词用词不当的原因之一。 诚然,Haskell只会在需要时评估某些东西,但是另一方面,当需要时,它会尽快进行评估。 在这里,Haskell需要评估输出,因为您要打印输出,因此它会尽可能地评估输出(一次一个字符),具有讽刺意味的是,它看起来很急!

更具体地说, interact不是用于实时用户输入,而是用于文件输入,在文件输入中,您使用bash将文件通过管道传输到可执行文件中。 应该运行这样的东西:

$ runhaskell Interactor.hs < my_big_file.txt > list_of_lengths.txt

如果要逐行缓冲,则可能必须手动进行,除非您想像Willem一样“欺骗”编译器。 这是一些非常简单的代码,可以按您期望的那样工作,但是请注意,它没有像interact那样的退出状态,后者会在EOF处终止。

main = do
  ln <- getLine -- Buffers until you press enter
  putStrLn ln   -- Print the line we just got
  main          -- Loop forever

lines "a"产生["a"]事实并不意味着如果您当前正在输入a ,那么这些lines只会将输入处理到列表["a"] 您应该将输入视为(可能)无限的字符列表。 如果提示正在等待用户输入,则提示将“阻塞”到下一个输入。

但是,这并不意味着像lines这样的函数无法部分解决结果。 lines以一种懒惰的方式实现,以便它处理字符流,并且每次看到新的行字符时,它将开始发出下一个元素。 因此,这意味着lines可以将无限的字符序列处理成无限的行列表。

但是,如果使用length :: Foldable f => fa -> Int ,则这需要对列表进行求值(但是不对列表的元素进行求值)。 因此,这意味着length只会从lines开始发出下一项的那一刻起就给出答案。

您可以使用seq (和变体)在执行某些操作之前强制对术语进行评估。 例如seq :: a -> b -> b a- seq :: a -> b -> b会将第一个参数评估为弱头范式(WHNF) ,然后返回第二个参数。

基于seq ,构建了其他功能,例如lists包的Data.Lists模块中的seqList :: [a] -> [a]

我们可以使用它来推迟评估,直到知道第一行,例如:

-- will echo full lines

import Data.Lists(seqList)

interact (unlines . map (\x -> seqList x x) . lines)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM