Haskell多么懒惰?
为什么以下不知道何时停止?
sum ([n^2 | n <- [1..], odd (n^2), n^2 < 100])
答案 0 :(得分:12)
这不是关于它是多么懒惰,而是它是否有任何方式可以知道n^2 < 100
一旦它是假的,它将永远不再是真的。它没有。
数组理解的这些部分是过滤器表达式,而不是停止条件。
答案 1 :(得分:7)
仅仅因为11 ^ 2超过100并不意味着13 ^ 2超过100.嗯......好吧,确实如此,但GHC应该如何解决这个问题呢?它是一个编译器,而不是任意数学真理的证明者。
答案 2 :(得分:2)
你给出的表达有效地消解了:
sum $ do
n <- [1..]
_ <- if odd (n^2) then [()] else []
_ <- if n^2 < 100 then [()] else []
return (n^2)
如果您从未见过List monad,那么这似乎是一种使用do
的奇怪方式,但它最终变为:
sum $ concatMap (\n -> if odd (n^2) && (n^2 < 100) then [n^2] else []) [1..]
其中concatMap
位于Prelude中(在早期版本中,您可以将其定义为(concat .) . map
,但现在它适用于任何Foldable
,而不仅仅是列表,因此更接近{{ 1}})。
现在关键是当Haskell看到这个功能时它会停止分析!计算机科学的一个定理是,证明任意一般函数属性的唯一一般方法是运行它们--Haskell不会窥视内部,也不会给(concat .) . fmap
任何方式来查看内部,一个函数来尝试确定它是否最终会为所有进一步的输入产生concatMap
!
计算机 dumb ,这是好:程序越聪明,就越难以在脑中建模。 []
是一个非常愚蠢的函数,它只是将函数参数应用于列表的每个元素,并将它们与concatMap
一起放在一起,就是这样。列表推导是列表monad的concat
表示法的非常愚蠢的语法替换,这就是他们所做的一切。 do
符号只是do
类型类中>>=
函数的非常愚蠢的语法替换,对于列表是Monad
。因为所有这些都非常愚蠢,你可以很容易地理解所有这些事情。