1.1.3 运算符组合式的求值
本章的一个目标就是把与过程性思维有关的问题区分出来。作为这方面的第一个情况,现在我们考虑运算符组合式的求值。解释器按下面的过程处理。
求值一个运算符组合式时,按下面的方式工作:
1.求值该组合式的各个子表达式。
2.把运算符指代的那个函数应用于有关的实参,也就是各个运算对象的值。
即使是这样一条简单规则,也显示出一些在计算过程中具有普遍意义的重要问题。首先,上面的第一步说明,为了实现对一个组合式的求值过程,我们必须首先对该组合式的每个运算对象执行这种求值过程。因此,从性质上说,这种求值过程是递归的,也就是说,在自己工作的步骤中,又包含了对这个规则本身的使用。
请特别注意,采用递归的思想可以多么简洁地描述深度嵌套的组合式的情况。如果不用递归,我们就需要把这种情况看成很复杂的计算过程。例如,求值:
需要把求值规则应用于4个不同的组合式。我们可以把这个组合式表示为树的形式,得到这个求值过程的图示,如图1.1所示。在这里,每个组合式用一个带分支的结点表示,由它发出的分支对应于组合式的运算符和运算对象。终端结点(那些不再发出分支的结点)表示运算符或者数。用树的观点看求值过程,我们可以设想那些运算对象的值向上穿行,从终端结点开始,在越来越高的层次组合起来。一般而言,我们会看到,递归是处理层次性结构(类似树这样的对象)的特别强有力的技术。事实上,“值向上穿行”形式的求值规则是一类更具普遍意义的计算过程的一个例子,这种计算过程称为树形积累。
图1.1 树表示,其中显示了各个子表达式的值
进一步观察告诉我们,反复应用第一个步骤,总会把我们带到求值中的一点,在这里遇到的不是组合式而是基本表达式,例如数或名字。我们按如下规定处理这些基础情况:
●数的值就是它们所表示的数值。
●名字的值就是在环境中关联于该名字的那个对象。
请注意这里的关键点:环境扮演的角色就是确定表达式中各个名字的意义。在如JavaScript这样的交互式语言里,如果没有环境提供信息,说(例如)表达式x+1的值也毫无意义,因为需要环境为符号x提供意义。正如我们将在第3章里看到的,一般的环境概念为求值的进行提供上下文,对我们理解程序的执行起着非常重要的作用。
请注意,上面给出的求值规则里没有处理声明。举个例子,对const x=3;求值,并不是把=运算符应用于它的两个实参,其中一个是名字x的值,另一个是3。声明的作用就是为x关联一个值(也就是说,const x=3;不是组合式)。
我们把const中的字母写成黑体,就是为表明它是JavaScript语言里的一个关键字。每个关键字都有特殊的意义,它们不能当名字使用。关键字或关键字组合要求JavaScript解释器以某种特殊方式处理相应的语句。不同语句形式各有自己的求值规则。各种类别的语句和表达式(每类各有相关的求值规则)组成了程序设计语言的语法。