组合完整的函数定义

我们已经完成了函数定义的各个部分,现在需要将它们整合起来。

首先是 while 表达式的内容:

(while (<= row-number number-of-rows)   ; true-or-false-test
  (setq total (+ total row-number))
  (setq row-number (1+ row-number)))    ; incrementer

配合 let 表达式的变量列表,这几乎完成了函数体的定义。但还需要最后一个细节,其作用较为微妙。

最后一步是在 while 表达式之后单独一行放置变量 total。否则整个函数的返回值将是 let 体内最后一个被执行表达式的值,也就是 while 的返回值,而它永远是 nil

这一点初看并不明显。看起来似乎递增表达式是整个函数的最后一个表达式,但该表达式其实是 while 体的一部分,是符号 while 开头的列表中的最后一个元素。而且整个 while 循环又是 let 体内的一个列表。

函数的整体结构大致如下:

(defun name-of-function (argument-list)
  "documentation..."
  (let (varlist)
    (while (true-or-false-test)
      body-of-while... )
    ... ))                    ; Need final expression here.

由于 let 除了被包含在 defun 内外没有嵌入其他列表,因此 let 的执行结果就是 defun 的返回值。但如果 whilelet 表达式的最后一个元素,函数将永远返回 nil,这并不是我们想要的结果。我们希望返回的是变量 total 的值。只需将该符号作为 let 开头列表的最后一个元素即可实现:它会在列表前面的元素执行完毕后被求值,此时它已经被赋予了正确的总数。

let 开头的列表写在一行上会更容易理解。这种格式可以清楚地看到,变量列表(varlist)和 while 表达式是 let 列表的第二、第三个元素,而 total 是最后一个元素:

(let (varlist) (while (true-or-false-test) body-of-while... ) total)

将所有部分组合后,triangle 函数的定义如下:

(defun triangle (number-of-rows)    ; Version with
                                    ;   incrementing counter.
  "Add up the number of pebbles in a triangle.
The first row has one pebble, the second row two pebbles,
the third row three pebbles, and so on.
The argument is NUMBER-OF-ROWS."
  (let ((total 0)
        (row-number 1))
    (while (<= row-number number-of-rows)
      (setq total (+ total row-number))
      (setq row-number (1+ row-number)))
    total))

执行该函数定义安装 triangle 后,你可以进行测试。下面是两个示例:

(triangle 4)

(triangle 7)

前四个数字之和为 10,前七个数字之和为 28。