SICP Ex 故事的寓意。 1.20?

Moral of the story from SICP Ex. 1.20?

在这个练习中,我们被要求使用第一个正常顺序然后应用顺序评估来跟踪 Euclid 算法。

(define (gcd a b)
  (if (= b 0)
      a
      (gcd b (remainder a b))))

我已经完成了手动跟踪,并通过互联网上可用的几种解决方案进行了检查。好奇在这里巩固一下演习的寓意

上面gcd中,注意b在函数体中重复使用了3次,加上这个函数是递归的。这就是正常顺序调用 remainder 的 18 次,而应用顺序只有 4 次调用。

因此,似乎当一个函数在其主体中多次使用一个参数(并且可能是递归的!如此处),然后在调用函数时不对其进行评估(即应用顺序),导致对同一事物的冗余重新计算。

请注意,该问题煞费苦心地指出特殊形式 if 在使用正常顺序时不会改变其行为;也就是说,if 总是第一个 运行;如果这没有发生,则本例中可能不会终止。

我很好奇我们在这里看到的延迟评估。

作为一个加号,它可以让我们处理无限的事情,比如流。 不利的是,如果我们有像这里这样的功能,它会导致效率低下。 要修复后者,似乎有两个概念上的选择。第一,将其包装在一些缓存其结果的数据结构中以避免重新计算。二、有选择地在知道参数的时候强制实现,否则会造成重复重算。

事实是,这两个选项看起来都不是很好,因为它们都代表额外的 "levers" 程序员必须知道如何使用和选择何时使用。

本书后面是否对所有这些都进行了更彻底的处理? 是否有任何直接合并这些点值得在这一点上明确(可能没有进入所有即将到来的细节)。

简短的回答是肯定的,本书后面将对此进行广泛介绍。

它在第 4 章中进行了最详细的介绍,我们在其中实现了 eval 和 apply,因此有机会修改它们的行为。例如练习 4.31 建议使用以下语法:

(define (f a (b lazy) c (d lazy-memo))

如您所见,这确定了三种不同的行为。

  • 参数 ac 具有应用顺序(它们在 被评估之前 它们被传递给 f)。
  • b是normal还是lazy,每次用到都会求值(但只有用到)。
  • d 懒惰,但它很有价值,所以它最多被评估一次。

尽管这种语法是可能的,但未被使用。我认为其哲学是语言具有预期的行为(应用顺序),并且仅在必要时默认使用正常顺序(例如,if 语句的结果和替代,以及创建流)。当需要(或希望)具有正常评估的参数时,我们可以使用 delayforce,如果有必要,还可以使用 memo-proc(例如练习 3.77)。

所以在这一点上,作者介绍了正常顺序和应用顺序的概念,以便我们在稍后深入了解细节时对它们有一定的了解。

从某种意义上说这是一个反复出现的主题,应用顺序可能更直观,但有时我们需要正常顺序。递归函数写起来比较简单,但是有时候我们需要迭代函数的性能。我们可以使用替代模型的程序更容易推理,但有时我们需要环境模型,因为我们需要可变状态。