考虑这个将列表中所有元素加倍的函数:
doubleMe [] = []
doubleMe (x:xs) = (2*x):(doubleMe xs)
然后考虑表达式
doubleMe (doubleMe [a,b,c])
很明显,在运行时,它首先扩展到:
doubleMe ( (2*a):(doubleMe [b,c]) )
(这很明显,因为据我所知不存在其他可能性)。
但我的问题是:到底为什么现在扩展到
2*(2*a) : doubleMe( doubleMe [b,c] )
而不是
doubleMe( (2*a):( (2*b) : doubleMe [c] ) )
?
直觉上,我知道答案:因为 Haskell 很懒。但有人能给我更准确的答案吗?
列表是否有什么特殊之处导致了这种情况,或者这个想法比列表更普遍?
最佳答案
doubleMe (doubleMe [a,b,c])
不会扩展为 doubleMe ( (2*a):(doubleMe [b,c]) )
。它扩展为:
case doubleMe [a,b,c] of
[] -> []
(x:xs) -> (2*x):(doubleMe xs)
即先展开外层函数调用。这是惰性语言和严格语言之间的主要区别:扩展函数调用时,您不会首先评估参数 - 而是用函数调用体替换函数调用,并暂时保留参数不变。
现在需要扩展doubleMe
,因为模式匹配需要知道其操作数的结构才能对其求值,因此我们得到:
case (2*a):(doubleMe [b,c]) of
[] -> []
(x:xs) -> (2*x):(doubleMe xs)
现在模式匹配可以替换为第二个分支的主体,因为我们现在知道第二个分支是匹配的分支。因此,我们用 (2*a)
代替 x
,用 doubleMe [b, c]
代替 xs
,得到:
(2*(2*a)):(doubleMe (doubleMe [b,c]))
这就是我们得出结果的方式。
关于haskell - Haskell 的惰性是如何发挥作用的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28302972/