为什么GHC有时会拒绝懒惰?

时间:2011-09-02 17:41:19

标签: haskell

这在tryhaskell.org上进入无限循环。我不知道为什么。

last $ filter (<100) $ [2..] >>= (\a -> if 0 == (length $ filter (== 0) $ map (mod a) $ [2..a-1]) then (return a) else [])

2 个答案:

答案 0 :(得分:9)

这并不是拒绝懒惰,只是没有办法让它知道你得到所有的素数&lt; 100,已经没有了。

如果序列看起来怎么样

1,2,... 99,100,101,102,5,103,......

换句话说,last无法预测未来。哦,我多么希望。

答案 1 :(得分:9)

让我们把事情分开,从内到外。从这开始:

last $ filter (<100) $ [2..] >>= (\a -> if 0 == (length $ filter (== 0) $ map (mod a) $ [2..a-1]) then (return a) else [])

首先考虑map (mod a) [2..a-1]。这只是有限列表的转换,所以这里没有问题,我们将从此处忽略它,将[..]置于其位置。因为我们只关心终止,所以任何有限列表都和另一个一样好。

同样适用于filter (== 0) [...]。这只能使列表更短,所以我们最终可能会得到一个空列表,但肯定是有限的。所以也要忽略它。现在考虑length [..] - 我们知道列表是有限的,所以这将终止正常,给出0或更多的答案。我们将忽略具体的答案并将?置于其位置。到目前为止仍然很好。

lambda \a -> if 0 == ? then return a else []在列表monad中使用return,因此这会将a替换为[a][],这恰好等同于{ {1}},但这是另一回事。同样,我们知道这很有用,所以我们忽略了细节并使用Maybe

monadic bind \a -> [a?]更有趣。 [2..] >>= \a -> [a?]这里是(>>=),第一个参数是无限列表。将每个元素映射到单个列表并连接显然不会改变任何内容,而映射到空列表会删除一个元素,因此这基本上只是concatMap。然而,事情在这里很简单,因为我们正在过滤无限列表而没有明确保证任何事情传递过滤器。如果你做filter,“结果”将没有元素,但计算将永远不会完成; filter (const False) [0..]输出中的[]来自查找输入列表的结尾,并且没有,因为它永远不会找到第一个元素,结果只是filter

所以事情已经成问题了。下一部分_|_会使事情变得更糟 - 我们从严格增加的列表中过滤元素,然后根据某些值过滤它,所以如果我们超过{{1在结果中,计算将挂起。

最后的侮辱是filter (<100) - 所讨论的列表仍然是无限的,但在某些时候会分歧而不是产生更多的元素,所以当我们要求最后一个元素时,我们可以看到它会失败因多种原因终止!

你想要做的是,首先,应用你的知识,列表严格增加,而不是过滤列表,只需将其前缀添加到所需的点 - 例如,100而不是last。请注意,如果结果中没有大于100的元素,这仍然会有分歧,因为takeWhile (<100)将不知道何时停止。