[SICP] 求值规则

来源:互联网 发布:网络商品交易改革 编辑:程序博客网 时间:2024/05/16 05:19

在Java语言学习中,通常不太关注求值规则。

(2+4*6)*(3+5+7)这种组合式的求值规则,通常归结为优先级问题;

if、for等的求值规则通常归结为语义。

函数式编程语言的Scheme,将这些归结为求值规则。按照丘奇的λ演算的函数应用:A、B是λ表达式,则 (A B) 也是λ表达式,表示将实参B带入函数A中。问题是:实参B带入函数A中时是否需要对实参B求值呢?

applicative-order Vs.normal-order 

  • ''evaluate the arguments and then apply'',称为应用顺序求值(applicative-order evaluation);严格求值 (Strict evaluation)、饥饿求值、
  • ''fully expand and then reduce'',称为normal-order evaluation(不知道为什么翻译成“正则序求值”,而后面3.5.4叫规范求值序)。短路计算、惰性求值。

因此,先定义primitive cases:
  • 数的值就是它们自己。=Java中的文字。
  • 内置操作符的值,是完成相应操作的指令序列。
  • 其他名字的值,是环境中关联到该名字的对象。

于是,

表达式求值【1.1.3  Evaluating Combinations】

组合式求值是函数应用求值(的简单情况)。按照应用序求值(applicative-order evaluation),即饥饿求值。

  1. 求子表达式的值(包括求最左的操作符的值、求各实参的值).
  2. 将操作符应用于实参。
函数的求值规则也是如此,不过在阅读代码时,通常我们简单的用代替法理解。

(define (square x)(* x x))

(define (sum-of-square x y)(+ (square x) (square y)))

(define (f a)(sum-of-square (+ a 1) (* a 2)))

那么(f 5)=

(sum-of-square (+ 5 1) (* 5 2))=

(sum-of-square 6 10) =

(+ (square 6) (square 10) =

(+ (* 6 6) (* 10 10) =

(+ 36 100) 

1.1.5介绍的代替模型,就是阅读代码的技巧。而实参如何代替形参,则是参数传递问题,这些东西放在后面介绍。3.2环境模型,其实Java中的栈-帧(stack frame)模型。[frame翻译成框架,有点炒蛋]


特殊块(特殊形式)求值

每一种特殊块(特殊形式)都有其自己的求值规则,通常为短路计算/惰性求值。

函数与执行流程

这个问题有意思。在C的教学中,有些人画流程图作为编写代码的“前期”工作。这一节,SICP说明“能够看清楚函数产生的后果的能力,对于成为程序设计专家是至关重要的”。

什么意思呢?我虽然不喜欢也不推荐学生画流程图,但是通过伪代码或源代码,我们能够知道过程/函数的执行流程,甚至我认为:能够看清楚函数产生的后果的能力,是一个基本能力。

但是,如果你解释器或编译器进行了优化,我需要知道函数的执行流程吗?如果需要,这就不是了解执行流程的问题,而是“深入Java虚拟机”的问题。

而在Scheme中,这一点(知道函数的执行流程)比较重要:因为递归。

如果你对递归不熟悉,其实,那么任何语言中,你都需要学习它的执行流程;如果你对递归比较熟悉,那么你需要知道Scheme中强调这一点的目的:因为想把递归变成循环以提高效率——尾递归。

除了递归之外,“知道函数的执行流程”是显而易见的。;over

以阶乘为例。递归代码如下。

(define (factorial n)

  (if (= n 1)

      1

      (* n(factorial (- n 1)))))

其执行流程, SICP中给出了一个图x。

但是其解释,我认为欠考虑。“The substitution model reveals a shape of expansion followed by contraction,indicated by the arrow in figure x”,“递归计算过程”本质上就是“轻率地”认为自己的较简单的情形是已知的,因而通过“展开和收缩”方式计算,在Java中(我们不考虑懒惰计算(lazy evaluation)时),这一解释是自然的。但是Scheme中,这里“展开和收缩”或“延迟计算”是递归带来的吗?不是。正如练习1.5所说明的,是采用正则序的if带来的。

如果将正则序作为讨论的默认条件(下面的迭代也使用if),尾递归使得“递归函数”按照迭代/循环的方式执行。因此,Scheme就不需要for、while等语法糖。

(练习1.9等)尾递归的一个特点,被替代的函数,作为递归表达式的第一个符号。

(define (factorial n)
  (fact-iter 1 1 n))

(define (fact-iter product counter max-count)
  (if (> counter max-count)
      product
      (fact-iter (* counter product)
                 (+ counter 1)
                 max-count)))

这里,函数fact-iter是一个递归函数,但是计算过程中由几个状态变量控制。We call this aniterative process.In general, an iterative process is one whose state can be summarized by afixed number of state variables, together with afixed rule that describes how the state variables should be updated as theprocess moves from state to state and an (optional) end test that specifiesconditions under which the process should terminate.

当然,用for将state variables、rule of update和an (optional) end test集中起来,这个语法糖,挺好。

练习1.9 – 1.10

 

树形递归

Fibonacci number:

(define (fib n)

  (cond ((= n0) 0)

        ((= n1) 1)

        (else(+ (fib (- n 1))

                (fib (- n 2))))))

这个代码解释了树形递归,但是从效率的角度,存在大量重复计算。如何设计尾递归代码?

 

其他,跳过。高阶函数见3.

太多数学题了。
0 0