如何在Haskell中过滤无限列表

时间:2012-11-24 12:06:02

标签: haskell

  

可能重复:
  Finite comprehension of an infinite list

我无法理解为什么ghci无法正确计算此代码?

[x | x <- [1..], x < 1000]

Ghci只是在最后一个号码处停止,我需要在命令行中断此过程以恢复正常状态。怎么了?我希望这段代码可以正常工作,因为haskell的懒惰评估。

4 个答案:

答案 0 :(得分:30)

[x | x <- [1..], x < 1000]相当于filter (< 1000) [1..];你需要takeWhile (< 1000) [1..]

那么filtertakeWhile之间有什么区别?

好吧,如果你试图评估整个结果 - 这就是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]