提及here的一个表现技巧是:
作为一个安全的默认值:在脊椎中懒惰,在树叶中严格。
我无法想象这样的数据结构。
如果我以Lists为例,如果我在叶子中严格禁止,那么脊柱是否会自动严格?
是否有任何数据结构示例,其中脊椎是懒惰的,叶子是严格的?
答案 0 :(得分:6)
“在脊椎中懒惰,在树叶中严格”是 API 的属性,而不是(仅)数据结构的属性。以下是它可能如何查找列表的示例:
module StrictList (StrictList, runStrictList, nil, cons, uncons, repeat) where
newtype StrictList a = StrictList { runStrictList :: [a] }
nil :: StrictList a
nil = StrictList []
cons :: a -> StrictList a -> StrictList a
cons x (StrictList xs) = x `seq` StrictList (x:xs)
uncons :: StrictList a -> Maybe (a, StrictList a)
uncons (StrictList []) = Nothing
uncons (StrictList (x:xs)) = Just (x, StrictList xs)
repeat :: a -> StrictList a
repeat x = x `seq` StrictList (let xs = x:xs in xs)
请注意,与内置列表相比,此API非常贫乏 - 这只是为了使插图保持较小,而不是出于根本原因。这里的关键点是你仍然可以支持像repeat
这样的东西,其中脊椎必然是懒惰的(它是无限的!)但是在发生任何其他事情之前评估所有叶子。许多可以生成无限列表的其他列表操作可以适应叶子严格版本(尽管不是全部,如您所见)。
你还应该注意到,不一定可能采取叶子懒惰,脊椎懒惰的结构,并以自然的方式将其变成严格的,严格的,脊柱懒惰的结构;例如一个人不能写一个通用的fromList :: [a] -> StrictList a
,以便:
fromList (repeat x) = repeat x
和runStrictList (fromList xs) = xs
xs
。(原谅我的惩罚,我是repeat
罪犯。
答案 1 :(得分:2)
这一点建议混合了两个相关但不同的想法。 Haskell程序员经常对这种区别不屑一顾,但这里很重要。
这是语义上的区别。如果f
,则函数f _|_ = _|_
strict ,否则为非严格函数。
这是一个实施问题,可能会产生重大的性能影响。延迟评估是实现非严格语义的一种方法。
实际上,这意味着数据结构应该是 strict 和 lazy 。数据结构的脊柱中的适量懒惰可能非常有用。有时它会提高性能的渐近性。它还可以提高缓存利用率并降低垃圾收集成本。另一方面,过多的懒惰(甚至在脊柱中,在某些情况下!)会导致延迟计算的有害累积。从API的角度来看,确保插入操作急切(因此严格)非常有用,这样您就可以知道存储在结构中的所有内容都已被强制。