我无法理解为什么ghci无法正确计算此代码?
[x | x <- [1..], x < 1000]
Ghci只是在最后一个号码处停止,我需要在命令行中断此过程以恢复正常状态。怎么了?我希望这段代码可以正常工作,因为haskell的懒惰评估。
答案 0 :(得分:30)
[x | x <- [1..], x < 1000]
相当于filter (< 1000) [1..]
;你需要takeWhile (< 1000) [1..]
。
那么filter
和takeWhile
之间有什么区别?
好吧,如果你试图评估整个结果 - 这就是ghci的作用,为了打印它 - 那么filter
将测试输入列表中的每个元素以确定它是否应该在输出列表中。在第一千元素之后?它继续进行测试。 filter
并不知道它不会突然遇到..., 12345, 12346, -7, 12348, ...
。
另一种看待它的方式是filter
一旦到达输入列表的末尾,就只能说“输出列表在这里结束”。如果您为它提供无限列表,则永远无法确定它是否已生成输出列表的所有元素。所以它似乎会挂起。
takeWhile
一旦到达不符合条件的元素,就会停止并终止其输出列表。
答案 1 :(得分:8)
您只是要求列表中的每个小于1000的数字。 您知道列表已排序,但您的算法没有利用此功能。编译器不会自动意识到您正在处理已排序的数据,并且无法推断一旦您看到一个至少为1000的数字,您就再也看不到一个小于该数字的数字。
正如其他人所指出的那样,takeWhile (< 1000) [1..]
利用你对列表特殊性的了解,一旦谓词对一个元素失败,就会特别停止对列表的检查(在这种情况下,一旦出现一个数字,至少遇到1000次)。请注意这是一个优化可用,因为[1..]
不是“只是一个列表”;它是一个包含特殊属性的列表(特别是它的排序)。
答案 2 :(得分:3)
由于您使用[1..]
生成无限,因此将使用您的条件x < 1000
检查此列表中的每个元素。这也意味着使用此条件检查此列表中大于1000的每个元素。
但你可以这样编写你的函数:
myFilteredList :: [Int] -> [Int]
myFilteredList [] = []
myFilteredList (x:xs) = if x < 1000 then x: myFilteredList xs else []
对于更一般的行为,您也可以将条件作为参数。
myFilteredList :: (a -> Bool) -> [a] -> [a]
myFilteredList cond [] = []
myFilteredList cond (x:xs) = if cond x then x: myFilteredList cond xs else []
myFilteredList (< 1000) [1..]
这正是预定义函数takeWhile
所做的。它将条件(a -> Bool)
和列表[a]
作为参数,并返回适合条件的列表的前缀。与myFilteredList
的定义一样,如果函数处理整个列表或者如果它到达列表中的第一个元素,那么函数就会终止,这不符合条件。
答案 3 :(得分:1)
检查x&lt; 1000用于决定在筛选列表中包括什么,而不是停止评估。换句话说,你给ghci一个无限的列表,它将为列表的每个元素做x <1000。如果要在谓词为True之前获取元素,请使用takeWhile。 Haskell是懒惰的并不重要,过滤后的列表会被评估,因为ghci会打印它。如果您想避免这种情况,请使用let
let l = [x | x <- [1..], x < 1000]