【问题标题】:The evaluation process in normal order(Scheme)?正常顺序的评估过程(方案)?
【发布时间】:2015-08-10 02:53:51
【问题描述】:

我正在学习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 次。 我认为这两个答案之间的主要区别在于,我认为一旦计算了余数,就不需要再计算它了,但答案似乎每次都计算它。这是编译器的问题吗?是不是太没效率了?

【问题讨论】:

    标签: scheme sicp


    【解决方案1】:

    edit:实际上,我所说的正常顺序是按名称调用。懒惰是按需调用,两者确实是正常的顺序。


    你的直觉对于应用顺序是正确的,其中参数被评估(即它们的值被找出)在函数调用之前。但在正常顺序中,它们仅在函数调用内部被评估时,当需要它们的值时——然后,这个值被遗忘。顺便说一下,记住找到的值的正常顺序称为 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))))
    ....
    

    你可以看到区别。

    【讨论】:

    • 啊,我明白了,所以你的意思是剩余的值不会按正常顺序保存。然后,如果我在参数中添加“延迟”或“强制”,那么即使按照正常顺序,剩余部分也应该被调用 4 次。
    • 你可以延迟它,但它是 system 调用它,并且 it 没有force 并且没有调用@ 987654325@在正常订单下。如果你可以在你的值之上添加一些记忆机制,那么系统仍然会要求它 18 次(或其他),但你的记忆值已经准备好并在那里等待第二次(所以remainder 确实会只被调用 4 次)。 -- 如果你把这种强制和延迟放入你的“系统”(即实施),那么你将实施惰性评估策略,而不是正常的顺序。
    • 啊,是的,我知道你的意思,所以如果我对系统使用惰性求值,剩余部分仍将被调用 18 次,但它只会计算 4 次,因为已存储的值。但我不明白你所说的懒惰评估或按需调用。我google了一下,我认为这只是功能实现的问题,它与正常顺序有什么关系?
    • 重读the wikipedia page,我猜都是正常的顺序,是按名字调用还是按需要调用是实现的问题。
    • 非常感谢。写得很好的解释!
    猜你喜欢
    • 1970-01-01
    • 2011-06-05
    • 2011-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多