2.3 Scheme表达式求值
我们已经知道了常量对象的求值规则,比如数字和字符串。常量的值就是它自己。你可能自己脑补了函数调用的求值规则。(procedure arg1 ... argn),这儿,procedure
是一个表达式,用来表示一个 Scheme 过程(函数),arg1 ... argn 同样是表达式,用来表示参数。一个可能的求值规则是:
- 找到
procedure
的值 - 找到 arg1的值
- ......
- 找到 argn的值
- 将
procedure
的值应用到 arg1 ... argn 的值上面
思考简单的函数调用(+ 3 4)
,+
的值是一个加法函数,3 的值就是 3,4 的值就是 4, 将 3 和 4 作为参数传递给函数 +
得到返回值 7, 所以 (+ 3 4)
的个表达式的值就是 7 这个对象。
重复这个过程,我们就可以得到嵌套的表达式 (* (+ 3 4) 2)
的值。*
的值是一个乘法函数,(+ 3 4)
的值上面已经得到了 7, 2 的值是2. 将乘法函数应用于 7 和 2,我们得到最终的值是 14.
这个求值规则对函数调用有效,对quote
表达式无效,因为quote
不对它的子表达式求值。对quote
表达式的求值类似于对常量的求值。
常量对象(字面量),过程(函数)以及引用(quote)是 Scheme 提供的众多语法形式中的三种。幸运的是,只有很少一部分其它语法形式需要 Scheme 直接理解,这些被称为“核心”语法形式,剩下的那些是“扩展”的语法定义。在本章的剩下的内容里,我们将要介绍剩下的那部分核心语法,以及少量的扩展语法。3.1节概括了核心语法并且介绍了语法扩展的机制。
在我们学习更多的语法及函数之前,关于函数调用的求值有两点值得注意。首先,上面给出的求值顺序是特定的,函数调用的子表达式从左到右依次求值,也就是 procedure 在 arg1 之前求值,而 arg1 在 arg2 之前求值。其实这不是强制的。一个 Scheme 求值器可以自由决定求值的顺序,从左到右或者从右到左,或者以别的任何顺序进行求值。甚至在同一个 Scheme 实现里,也会对不同的过程使用不同的求值顺序。
第二点要注意的是,procedure
采用与 arg1 ... argn 相同的方式求值。procedure
是一个变量名,用来命名一个过程。然而处在 procedure
位置上的不一定必须是一个变量,也可以是另外一个表达式。在练习 2.2.3 里,要求你判断这个表达式的值((car (list + - * /)) 2 3)
,在这里,作为函数被调用的并不是某个被命名的函数,而是这个表达式(car (list + - * /))
,它的值就是加法函数+
。
练习 2.3.1
写出下面的表达式的求值步骤
((car (cdr (list + - * /))) 17 5)