组合式的求值

解释解释器如何求值组合式:先求值子表达式再应用运算符,并用递归树展示求值过程,最后说明 define 作为特殊形式的例外。

“求值”是什么意思?

当我们在解释器中输入 (+ 2 3) 这样的 Racket 表达式时,我们人类立刻就知道它其实就是 5。但在计算机眼里,看到的只是一个左括号、一个加号、数字二、数字三和一个右括号。它是如何从 Racket 表达式得到值 5 的呢?它会对表达式进行求值(evaluate),并由此得到值 5。那么,它又是怎样求值的呢?

解释器如何求值

解释器的求值方式一开始可能有点令人困惑,但很快你就会明白。要求值一个 Racket 表达式,先求值它的各个子表达式(subexpression)。换句话说,先把操作数完整地求值,然后再应用运算符。遇到过程调用时,把运算符应用于操作数,然后重复这一过程。请注意,求值是递归的——为了求值一个表达式,我们需要先求值它的子表达式;为了求值这些子表达式,我们又要求值它们的子表达式,如此继续,直到到达一个(基本)过程。

示例:递归树

我们来试着求值下面这个表达式:

(* (+ 2 (* 4 6))
   (+ 3 5 7))

这是一个相当复杂的表达式,如果没有递归,求值它会非常困难。求值这个表达式,需要把求值规则应用四次。如果把求值过程表示成一棵树,理解起来就会容易一些。这棵树与真实的树不同:它的根在空中,树枝伸向地面。

每个组合式用一个节点表示,从节点伸出的分支对应各个子表达式。末端的树枝是运算符或数字。我们可以想象操作数的值从树的底部向上”游动”,在每个分支处得到求值,产生一个新值,再到更高一层被求值。

递归树

更详细的解释见 eval 的词条,在讲解替换模型的各节中还会有进一步说明。

define 呢?

define 又怎么样呢?原来,普通的求值规则对 define 并不适用,因为 (define x 3) 并不是把 define 应用于两个实参,而是把 x 的值存为 3。define 是一种所谓的特殊形式(special form),而特殊形式是求值规则唯一的例外。

测一测

以下哪一项不是 Racket 的基本函数(primitive function)?

  • +
  • if
  • not
  • square

答案与解析

正确答案是 if。if 是一种特殊形式(special form)! 其他选项都不对:+、not 和 square 都遵循常规的求值规则。