我正在阅读:Haskell interact function
所以我尝试了
interact (unlines . map (show . length) . lines)
它按我的预期工作。我输入一些内容,按Enter键,然后在提示处打印出长度。
所以我想尝试使其简单地重复我键入的内容,所以我尝试了
interact (unlines . map id . lines)
但是现在它会重复我键入的每个字符。为什么呢?我以为诀窍在lines
之后,接着是unlines
-但事实并非如此。 lines "a"
会产生["a"]
,所以当我开始输入输入时,第一个函数又如何呢?它不只是立即给出“ 1”作为输出?显然,我对“发现字符串的长度不是这样-在生成任何输出之前必须知道整个字符串”感到误解。
答案 0 :(得分:3)
这与延迟评估有关。我将尝试以尽可能直观的方式对此进行解释。
在编写interact (unlines . map (show . length) . lines)
时,每次输入一个字符时,我们实际上都不知道下一个输出字符是什么,除非您按Enter键。因此,您将获得预期的行为。
但是,在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
答案 1 :(得分:3)
lines "a"
产生["a"]
的事实并不意味着如果您当前正在输入a
,那么lines
只会处理列表["a"]
的输入。 。您应该将输入视为(可能)无限的字符列表。如果提示正在等待用户输入,则提示将“阻塞”下一次输入。
但是 not 并不意味着像lines
这样的函数可以 not 已经部分解析了结果。 lines
以一种惰性方式实现,以便处理字符流,并且每次看到换行符时,它就会开始发出下一个元素。因此,这意味着lines
可以将无限个字符序列处理成无限的行列表。
但是,如果您使用length :: Foldable f => f a -> Int
,则需要对列表进行评估(但是,不是列表的 elements )。因此,这意味着length
只会从lines
开始发出下一个项目的那一刻起给出答案。
您可以使用seq
(和变体)在执行某些操作之前强制对术语进行评估。例如,seq :: a -> b -> b
会将第一个参数求值为弱头范式(WHNF),然后返回第二个参数。
基于seq
,还构建了其他功能,例如seqList :: [a] -> [a]
包的Data.Lists
module中的lists
。
我们可以使用它来推迟评估,直到知道第一行为止,例如:
-- will echo full lines
import Data.Lists(seqList)
interact (unlines . map (\x -> seqList x x) . lines)