[英]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.