我想知道在Haskell中如何评估list-comprehension。阅读此Removing syntactic sugar: List comprehension in Haskell之后:Haskell Lazy Evaluation and Reuse我仍然不明白是否
[<function> x|x <- <someList>, <somePredicate>]
实际上完全等同于(不仅仅是结果,而是评估中)
filter (<somePredicate>) . map (<function>) $ <someList>
如果是这样,这是否意味着它可以大大减少时间复杂度,以构建只有所需元素的所需列表? 此外,这如何在无限列表方面工作?具体来说:我假设像:
[x|x <- [1..], x < 20]
将在有限的时间内进行评估,但是对于编译器来说,在某些值上面没有更多元素满足谓词需要的事实是多么“明显”呢?将
[x|x <- [1..], (sum.map factorial $ digits x) == x]
工作(参见项目欧拉问题34 https://projecteuler.net/problem=34)。显然有一个上限,因为x * 9上有些x! &LT; 10 ^ n -1总是成立,但是我需要提供该绑定还是编译器会找到它?
答案 0 :(得分:3)
特定的无限序列没有更多与谓词匹配的元素,这一事实并不明显。将列表传递给filter
时,它无法知道元素的任何其他属性,而不能将元素传递给谓词。
您可以编写自己的Ord a => List a
版本,可以将序列描述为升序与否,以及版本filter
,可以使用此信息停止查看超过特定阈值的元素。不幸的是,列表推导不会使用其中任何一个。
相反,我使用takeWhile
和没有谓词/普通map
的理解的组合。在takeWhile
参数中的某处,您将为编译器提供有关预期上限的信息;对于许多 n 十进制数字,它将是10 ^ n 。
答案 1 :(得分:3)
[<function> x|x <- <someList>, <somePredicate>]
应始终评估与
相同的结果filter (<somePredicate>) . map (<function>) $ <someList>
然而,无保证这就是编译器实际执行的方式。 The section on list comprehensions in the Haskell Report仅提及 列表推导应该做什么,而不是 应该如何工作。所以每个编译器都可以随意开发,因为它的开发人员最好。因此,您不应假设列表推导的性能或编译器将进行任何优化。