在Haskell中,经常提到术语 spine strictness 与懒惰评估有关。虽然我对这意味着一个模糊的理解,但是对于具体的解释更好:
答案 0 :(得分:30)
这是一个例子
> length (undefined : 3 : 4 : undefined : [])
4
> length (2 : 3 : 4 : 5 : undefined)
<<loop>>
第一个列表包含底部元素,但列表的“形状”已完全定义。粗略地说,每个列表单元格都有一个明确定义的“指针”指向其下一个元素。这种“形状”称为脊柱。
相比之下,第二个列表已完全定义了元素,但未定义其主干。这是因为它不以空列表[]
结尾,而是以非终止表达式undefined
结束。在这种情况下,脊柱未定义。
函数length
关心的是脊椎,而不是元素。所以它能够在第一种情况下工作(感谢懒惰),但不是第二种情况。我们说length
在脊椎中是严格的,但不在列表的元素中。
类似地,在树数据结构中,书脊是树的“形状”。某些功能(如树高)可以在不检查元素的情况下编写,但只能编写脊柱。这种功能在脊柱上是严格的。
虽然某些功能必须是脊柱严格的(例如长度),但其他功能可以以脊柱严格或脊椎延迟的方式编写。例如,列表上的map
是棘手的:它将在访问输入的所有主干之前返回输出的第一个元素。
map' :: (a->b) -> [a] -> [b]
map' _ [] = []
map' f (x:xs) = (f x :) $! map' f xs
这是否有益取决于具体情况。考虑
-- apply n times function f
iter n f = foldr (.) id $ replicate n f
list1 = iter 1000 (map succ) [1..10]
list2 = iter 1000 (map' succ) [1..10]
如果我要求head list1
,我将强制仅在列表的第一个元素处应用1000个地图。这意味着在此之后将有1000个分配的内存在内存中占用空间。
相反,head list2
将强制在整个列表中应用1000个地图。因此,所有1000个thunks都可以立即进行垃圾回收,回收内存。