【问题标题】:Haskell - Why does this list comprehension return an infinite list?Haskell - 为什么这个列表理解返回一个无限列表?
【发布时间】:2021-01-16 00:37:52
【问题描述】:
[3 * x | x <- [1 ..], 3 * x < 20]

我真的不明白为什么会这样

[3,6,9,12,15,18

并没有找到终点

【问题讨论】:

  • 你怎么知道没有一个数字真的在列表[1 ..] 不满足你的条件?毕竟,如果您将[1 ..] 更改为([1 .. 100000] ++ [2]) 会怎样?或者,如果你有一个“环绕”的数字类型,当它变得超高时,它又变低了怎么办? GHC 肯定不知道,所以它会继续尝试。

标签: haskell list-comprehension


【解决方案1】:

语义

[3 * x | x <- [1 ..], 3 * x < 20]

是尝试[1..]的所有元素,并保留那些满足过滤条件3*x&lt;20的元素。

人类可以看到,在第一个伪造条件的 x 之后,尝试所有更大的值是没有意义的,但 Haskell 无论如何都会尝试这些,并陷入一种无限循环。

这是因为,在一般情况下,条件可能会再次变为真,例如

[3 * x | x <- [1 ..], 3 * x < 20 || x == 1000000 ]

一般来说,检测是否没有更多的解决方案是无法确定的,因此 Haskell 和任何其他编程语言一样,不能选择在最后一个解决方案之后停止。

如果您希望列表在第一个不满足过滤条件的值之后停止,请使用takeWhile

takeWhile (< 20) [3 * x | x <- [1 ..]]

【讨论】:

  • 很好的解释!尽管出于好奇,您知道是否有办法在列表理解中完成所有这些操作?
  • @DavLin:不完全是。有时你可以使用像data Nat = Zero | Succ Nat这样的惰性数字类型,所以如果你有像genericLength someList &lt; (10 :: Nat)这样的条件,即使someList是无限的,它也可以通过只检查10个元素来返回;而如果someList 是无限的,length 不会停止,因为它的结果Int 是严格的。在这种情况下,即使使用惰性数字类型,列表也永远不会结束:推导式不断产生元素,并且不断被过滤掉。所以你真的想要输出上的takeWhile 或输入上的上限,比如[1 .. (20 `div` 3) + 1]
  • @DavLin 见this question 但简单地说,不,你不能在列表理解中这样做。我在那里写的作为答案的 hack 是一种笑话,它在技术上使得它成为可能,但代价是大大增加了代码的复杂性,启用了一堆扩展,需要一些自定义库,并且使用了奇怪的语义。永远不要使用它。
猜你喜欢
  • 2010-10-25
  • 2015-01-10
  • 2010-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多