Haskell函数示例,为什么无限列表不会停止

时间:2014-01-09 22:24:47

标签: haskell functional-programming

为什么不映射sqrt [1 ..]不能给出无限递归? 我怎样才能更好地理解haskell?

sqrtSums :: Int
sqrtSums = length ( takeWhile (<1000) (scanl1 (+) (map sqrt[1..]))) + 1

2 个答案:

答案 0 :(得分:2)

懒惰将列表转换为流

Haskell中的列表表现得好像它们具有内置迭代器或流接口,因为默认情况下整个语言使用延迟评估,这意味着只有在调用函数需要时才计算结果。

在您的示例中,

sqrtSums = length ( takeWhile (<1000) (scanl1 (+) (map sqrt[1..]))) + 1

好像length不断向takeWhile询问另一个元素,
scanl1询问另一个元素,
map询问另一个元素,
[1..]询问另一个元素。

一旦takeWhile得到的内容不是<1000,它就不会向scanl1询问更多元素,因此[1..]永远不会得到完全评估。

的thunk

未评估的表达式称为 thunk ,从thunk中获取答案称为 reduce 。例如,thunk [1..]首先减少到1:[2..]。在许多编程语言中,通过编写表达式,可以强制编译器/运行时计算它,但不能在Haskell中计算。我可以写ignore x = 3并执行ignore (1/0) - 我会得到3而不会导致错误,因为1/0不需要计算来生成3 - 它只是并没有出现在我想要制作的右手边。

同样,您不需要在列表中生成超过131的任何元素,因为那时总和已超过1000,而takeWhile生成一个空列表[],此时{{1} }}返回length130生成sqrtSums

答案 1 :(得分:1)

Haskell评估表达式 lazily 。这意味着仅在需要时才进行评估。在此示例中,takeWhile (< 1000)反复要求scanl1 (+) (map sqrt [1..])的答案,但停止后,其中一个答案超过1000。在这个开始发生的那一刻,Haskell不再评估更多(真正无限的)列表。

我们可以通过从这个例子中删除一些部分来看到这一点

>>> takeWhile (< 10) [1..]
[1,2,3,4,5,6,7,8,9]

这里我们有一个代表无限列表的表达式([1..]),但takeWhile确保总表达式只需要那些那些无数的值。没有takeWhile Haskell将尝试打印整个无限列表

>>> [1..]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24Interrupted.

但是我们再一次注意到Haskell只需要每个元素一个接一个,因为它需要它们才能打印出来。在一种严格的语言中,我们会在打印第一个答案之前试图在内部表示无限列表。