我有一个功能
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
答案 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。