我正在学习SICP,ex1.20给出了以下代码:
(define (gcd a b)
(if (= b 0)
a
(gcd b (remainder a b))))
当我们分别以应用顺序和正常顺序运行(gcd 206 40)
时,问题会询问剩余时间。我可以弄清楚剩余被按照应用顺序被召唤4次,但是我不明白为什么它在正常秩序时会变成18。在我看来,首先调用(gcd 40 (remainder 206 40))
,接下来我们需要计算(remainder 206 40)
,即6,如果我们想知道我们去哪个分支,然后(remainder 40 6)
,那么在,它也是4次。但答案给出了以下过程:
(gcd 206 40)
(if (= 40 0) ...)
(gcd 40 (remainder 206 40))
(if (= (remainder 206 40) 0) ...)
(if (= 6 0) ...)
(gcd (remainder 206 40) (remainder 40 (remainder 206 40)))
(if (= (remainder 40 (remainder 206 40)) 0) ...)
(if (= 4 0) ...)
(gcd (remainder 40 (remainder 206 40)) (remainder (remainder 206 40) (remainder 40 (remainder 206 40))))
(if (= (remainder (remainder 206 40) (remainder 40 (remainder 206 40))) 0) ...)
(if (= 2 0) ...)
(gcd (remainder (remainder 206 40) (remainder 40 (remainder 206 40))) (remainder (remainder 40 (remainder 206 40)) (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))))
(if (= (remainder (remainder 40 (remainder 206 40)) (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))) 0) ...)
(if (= 0 0) ...)
(remainder (remainder 206 40) (remainder 40 (remainder 206 40)))
所以总共是18次。 我认为两个答案之间的主要区别在于,我认为一旦计算出余数,就不再需要计算,但答案似乎每次都可以计算出来。这是编译器的问题吗?这不是太有效吗?
答案 0 :(得分:2)
编辑:实际上,我所说的正常订单是按名称调用。懒惰是按需调用,两者都是正常的顺序。
你的直觉对于应用顺序是正确的,其中在函数调用之前评估参数(即它们的值被发现)。但是按照正常的顺序,它们只在函数调用中在内部进行评估,当需要它们的值时 - 然后,这个值被遗忘了。顺便说一下,记住找到的值的正常顺序称为 lazy 评估。
因此,评估的适用顺序的减少顺序是
> (gcd 206 40)
= (if (= 40 0) 206 (gcd 40 (remainder 206 40)))
= (gcd 40 (remainder 206 40))
> (remainder 206 40) => 6
= (gcd 40 6)
= (if (= 6 0) 40 (gcd 6 (remainder 40 6)))
= (gcd 6 (remainder 40 6))
> (remainder 40 6) => 4
= (gcd 6 4)
....
但是对于正常的顺序,它将是
> (gcd 206 40)
= (if (= 40 0) 206 (gcd 40 (remainder 206 40)))
= (gcd 40 (remainder 206 40))
= (if (= (remainder 206 40) 0) 40
(gcd (remainder 206 40) (remainder 40 (remainder 206 40))))
....
你可以看到差异。