有经验的 Lisp 程序员,由上而下(top-down)也由下而上 (bottom-up)地工作。当他们朝着语言撰写程序的同时,也打造了一个更适合他们程序的语言。通过这种方式,语言与程序结合的更好,也更好用。
写来扩展 Lisp 的操作符称为实用函数(utilities)。当你写了更多 Lisp 程序时,会发现你开发了一系列的程序,而在一个项目写过许多的实用函数,下个项目里也会派上用场。
专业的程序员常发现,手边正在写的程序,与过去所写的程序有很大的关联。这就是软件重用让人听起来很吸引人的原因。但重用已经被联想成面向对象程序设计。但软件不需要是面向对象的才能重用 ── 这是很明显的,我们看看程序语言(换言之,编译器),是重用性最高的软件。
要获得可重用软件的方法是,由下而上地写程序,而程序不需要是面向对象的才能够由下而上地写出。实际上,函数式风格相比之下,更适合写出重用软件。想想看 sort
。在 Common Lisp 你几乎不需要自己写排序程序; sort
是如此的快与普遍,以致于它不值得我们烦恼。这才是可重用软件。
图 6.1 实用函数
> (single? '(a))
T
而后一个函数 append1
和 很像,但在列表后面新增一个元素,而不是在前面:
下个实用函数是 map-int
,接受一个函数与整数 n
,并返回将函数应用至整数 0
到 n-1
的结果的列表。
这在测试的时候非常好用(一个 Lisp 的优点之一是,互动环境让你可以轻松地写出测试)。如果我们只想要一个 0
到 9
的列表,我们可以:
(0 1 2 3 4 5 6 7 8 9)
然而要是我们想要一个具有 10 个随机数的列表,每个数介于 0 至 99 之间(包含 99),我们可以忽略参数并只要:
map-int
的定义说明了 Lisp 构造列表的标准做法(idiom)之一。我们创建一个累积器 ,初始化是 nil
,并将之后的对象累积起来。当累积完毕时,反转累积器。
> (filter #'(lambda (x)
'(1 2 3 4 5 6 7))
(12 14 16)
另一种思考 filter
的方式是用通用版本的 。
图 6.1 的最后一个函数, most
,根据某个评分函数(scoring function),返回列表中最高分的元素。它返回两个值,获胜的元素以及它的分数:
如果平手的话,返回先驰得点的元素。
注意图 6.1 的最后三个函数,它们全接受函数作为参数。 Lisp 使得将函数作为参数传递变得便捷,而这也是为什么,Lisp 适合由下而上程序设计的原因之一。成功的实用函数必须是通用的,当你可以将细节作为函数参数传递时,要将通用的部份抽象起来就变得容易许多。
本节给出的函数是通用的实用函数。可以用在任何种类的程序。但也可以替特定种类的程序撰写实用函数。确实,当我们谈到宏时,你可以凌驾于 Lisp 之上,写出自己的特定语言,如果你想这么做的话。如果你想要写可重用软件,看起来这是最靠谱的方式。