一个用while
循环打印数字列表元素的例子可以用递归的方式重写。下面是代码,包括一个表达式,将变量animals
的值设置为一个列表。
如果你在Emacs的Info中阅读此内容,你可以直接在Info中评估这个表达式。否则,你必须将示例复制到*scratch*缓冲区,并在那里逐个评估每个表达式。使用C-u C-x C-e来评估(print-elements-recursively animals)
表达式,以便结果打印在缓冲区中;否则,Lisp解释器将尝试将结果压缩成回显区域的一行。
此外,在print-elements-recursively
函数的最后一个闭合括号之后,在注释之前将光标放置在此处。否则,Lisp解释器将尝试评估注释。
(setq animals '(gazelle giraffe lion tiger)) (defun print-elements-recursively (list) "将LIST的每个元素单独打印到一行上。 使用递归。" (when list ; do-again-test (print (car list)) ; body (print-elements-recursively ; recursive call (cdr list)))) ; next-step-expression (print-elements-recursively animals)
print-elements-recursively
函数首先测试列表中是否有内容;如果有,函数将打印列表的第一个元素,即列表的CAR。然后,函数调用自身,但将自身作为参数传递,而不是整个列表,而是列表的第二个及后续元素,即列表的CDR。
换句话说,如果列表不为空,函数调用另一个与初始代码相似但是不同执行线程的代码实例,其参数与第一个实例不同。
再换一种说法,如果列表不为空,第一个机器人组装第二个机器人并告诉它该做什么;第二个机器人是第一个机器人之外的另一个个体,但是是相同型号。
当进行第二次评估时,when
表达式将被评估,如果为真,则打印作为其参数接收到的列表的第一个元素(这是原始列表的第二个元素)。然后,函数使用它调用的列表的CDR调用自身,这是原始列表的CDR的CDR(第二次调用时)。
请注意,尽管我们说函数“调用自身”,但我们的意思是Lisp解释器将装配并指导程序的新实例。新实例是第一个的克隆,但是是独立的个体。
每次函数调用自身时,它都是在原始列表的较短版本上进行的。它创建一个在较短列表上运行的新实例。
最终,函数在空列表上调用自身。它创建一个参数为nil
的新实例。条件表达式测试list
的值。由于list
的值是nil
,when
表达式测试为假,因此不会评估then部分。因此,整个函数返回nil
。
当在*scratch*缓冲区中评估表达式(print-elements-recursively animals)
时,你将看到以下结果:
gazelle giraffe lion tiger nil