关于懒惰的困惑

时间:2010-05-25 11:14:39

标签: haskell functional-programming lazy-evaluation

我有一个功能

myLength = foldl (\ x _ -> x + 1) 0

失败,堆栈溢出,输入大约10 ^ 6个元素(myLength [1..1000000]失败)。我相信这是因为当我用foldl'替换foldl时,thunk积累,它起作用。 到目前为止一切都很好。

但现在我有另一个功能来反转列表:

myReverse = foldl (\ acc x -> x : acc) []

使用懒惰版本foldl(而不是 of foldl')

当我这样做的时候 myLength . myReverse $ [1..1000000]。 这次它运作正常。我不明白为什么foldl适用于后一种情况而不适用于前者?

为了澄清这里myLength使用foldl',而myReverse使用foldl

1 个答案:

答案 0 :(得分:3)

这是我最好的猜测,虽然我不是Haskell内部的专家(还)。

在构建thunk时,Haskell会在上分配所有中间累加器变量。

myLength中执行添加时,需要将堆栈用于中间变量。见this page。摘录:

  

当我们最终评估z1000000时,问题就出现了:

     

请注意,z1000000 = z999999 +   1000000.所以1000000被推到了堆栈上。然后评估z999999。

     

请注意,z999999 = z999998 + 999999。   因此999999被推入堆栈。然后   评估z999998:

     

请注意,z999998 = z999997 + 999998。   所以999998被推上了堆栈。然后   评估z999997:

但是,在执行列表构建时,这就是我认为发生的事情(这是猜测开始的地方):

  

评估z1000000时:

     

请注意,z1000000 = 1000000:   z999999。所以1000000存储在里面   z1000000,以及链接(指针)   到z999999然后评估z999999。

     

请注意,z999999 = 999999:z999998。   所以999999存储在z999999中,   以及指向z999998的链接。然后   z999998已被评估。

     

请注意,z999999,z999998等从尚未评估的表达式转换为单个列表项是Haskell的日常事情:)

由于z1000000,z999999,z999998等都在堆上,因此这些操作不使用任何堆栈空间。 QED。