【发布时间】:2014-12-10 03:03:47
【问题描述】:
为了更好地理解评估模型,我玩弄了一些定义,并为列表的长度写了两个。
天真的定义:
len :: [a] -> Int
len [] = 0
len (_:xs) = 1 + len xs
严格(和尾递归)定义:
slen :: [a] -> Int -> Int
slen [] n = n
slen (_:xs) !n = slen xs (n+1)
len [1..10000000] 大约需要 5-6 秒来执行。slen [1..10000000] 0 大约需要 3-4 秒来执行。
我很好奇为什么。在我检查性能之前,我确信它们的性能大致相同,因为len 最多只能再评估一个 thunk。出于演示目的:
len [a,b,c,d]
= 1 + len [b,c,d]
= 1 + 1 + len [c,d]
= 1 + 1 + 1 + len [d]
= 1 + 1 + 1 + 1 + len []
= 1 + 1 + 1 + 1 + 0
= 4
和
slen [a,b,c,d] 0
= slen [b,c,d] 1
= slen [c,d] 2
= slen [d] 3
= slen [] 4
= 4
是什么让slen 明显更快?
附:我还写了一个尾递归惰性函数(就像 slen 但惰性)作为尝试关闭原因 - 也许是因为它是尾递归 - 但它的执行与天真的定义大致相同.
【问题讨论】:
-
len的最后一步不是 O(1)。将 n 个数字相加是 O(n)。slen也使用 O(n) 内存,而len使用 O(1) 内存。 -
@DavidYoung 哦,我明白了!欢迎您将其写为答案。你能解释一下内存消耗吗? (或者只是参考我可以进一步理解的地方)。非常感谢!
-
抱歉,我弄反了。
len使用 O(n) 内存,slen使用 O(1) 内存。
标签: performance haskell ghc lazy-evaluation evaluation