Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:* d: v, y+ R$ x) x/ K% x- r
, Z% m2 q5 ^5 k+ u" a" l: @既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? 2 {8 _7 i2 M$ v% A7 S$ D, L8 Q
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
$ \; y& q- Q, g2 [9 ^& h! l(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……), z9 w9 T2 d4 y4 s
/ b+ I( i6 G3 L
这一集中,我们将描述几个常用的函数,并给出它们的简单实现; k1 u% t( S3 |* C' x0 q0 n
e- I' m# D. E9 k首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?2 g$ m, q2 j9 g [4 a& _
, ?. J& h" \+ A+ v可能有些读者已经想到了,取第二个元素可以采用如下形式:3 G- D0 x& t/ s1 m D2 K( p
1 A- ^- P2 o9 l5 D/ j(car (cdr x))
0 G! O! x n) U7 B
b2 ^: x# J' q5 Z: N: y同理,取第三个元素是这样的:; v) S$ h2 m, |+ ?3 l) `2 F4 v; I
% @5 ~5 L7 Z0 P- h; A# ~
(car (cdr (cdr x)))" t' `; W$ n7 N6 K
. K* i$ ]+ s( q5 V8 j. l7 h5 n
事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:6 S) o/ a8 `9 q. H1 d- w2 N( e! v
) K: i8 b- E' ^0 ~> (cadr '((a b) (c d) e))
4 z" _8 A; \: Q: k" ^" r3 i(c d)
M4 n d& f/ i1 L> (caddr '((a b) (c d) e))
% O# x v8 d7 Xe5 y# f* e$ `2 F
> (cdar '((a b) (c d) e)); y6 _1 O/ F" o4 t7 c, | v6 t7 _
(b)
4 U, z# `/ n/ c I4 A' V9 A# S6 r, Q4 l+ R3 q
另外,使用(list e1 e2 ... en)来表示
* j7 N' y' v' _- a' H(cons e1 (cons e2 (... (cons en '())...)))
& I E! f; F M' m6 O# E' T( x2 r" p0 |5 ~( C) o: Z
> (cons 'a (cons 'b (cons 'c '())))
4 R) k a; l4 E8 F' Z(a b c)# [1 V6 T5 P {" @
> (list 'a 'b 'c)3 Z9 C5 ^; l, B$ Q5 F7 d
(a b c)9 y7 C3 k2 R/ g: k% c" M `9 x
d+ a( a: X( P. M现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。2 P% k/ \9 q0 P
8 B) Q4 i( G" Y7 L: T# q+ v K
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)8 b3 V! S% H% L% P
0 t; p4 R5 \% }( O$ Y
(null x),测试x是否为空表。例如:
% t4 B& x; Q/ Q! n" Z> (null 'a)' X( v. [- R% Q$ Q+ M; F r
()5 n, K) w( H/ x! Z1 ~6 a2 y5 I
> (null '())
2 P; u4 f* V, Y4 n5 b2 {) Z/ K5 L xt 2 O3 _& q4 f* W$ N
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。( E# c- _/ a+ t6 b
> (and 'a 'b)" g/ K" I3 q4 R& L( c7 [
t
( c& y1 M5 d! ~* x2 ]! Q- y6 u' |; m> (and (atom 'a) (eq 'b 'c))) N+ `, l4 h) u- Y- E$ G
() ! A5 u1 o5 e' A1 L& W
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:& M! R, k. V+ H( ^! c: t/ R
> (not 'a)
8 A" ^8 ^, A* u# g7 [. H()
; Z% S; v8 j# a! h; {. `> (not (eq 'a 'b)): p& p4 w9 O2 s9 R
t 4 j( N2 h5 l8 q7 ?
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:/ U% _6 {) i2 G& T: F% i
> (append '(a b) '(c d))
+ |7 N* _/ s/ ~1 m(a b c d)) C/ l& T2 @ N( ]8 c1 @
> (append '() '(x y))" p- @# ] d0 x( I' K; d2 _
(x y) * ]9 R' ?0 M" Z+ v
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:5 G" b/ ^& |& p. t) m
> (pair '(a b c) '(x y z))
& @0 S8 g9 J! s((a x) (b y) (c z)) - d/ \- _1 O% L/ ]
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:: c5 Y' `. c9 H
> (assoc 'a '((a x) (b y))). |% R/ I9 A5 Y, N2 }. f
x+ z( M# F1 h- \2 a& a7 I
> (assoc 'a '((a (foo bar)) (b y) (c z)))
; d- w. M: }0 W/ I(foo bar)
3 u6 U. I4 B7 _ |5 d9 V( B7 I3 Z(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:' I$ Q; y/ u, y4 ]
> (subst '(x y) 'b '(a b (a b c) d))
3 x, s* X) S: C(a (x y) (a (x y) c) d)
' l7 y5 }0 n+ l) M# @( \2 E6 L下面我们给出这些常用函数的简单实现:7 `3 s* Y# z, I8 L; E8 ?2 z5 c
4 q6 m, y1 _: ?9 k1 y8 H, f
(defun null (x)
+ A. s7 G6 ?/ ] (eq x '()))
6 u* B: ?2 k2 g! H" E- P0 H4 u/ ~(defun and (x y)
. o% D `9 E8 f# R! w" @5 R { (cond (x (cond (y 't) ('t '())))
. i9 ]9 T$ _+ z ('t '())))
7 c( V5 C8 F- M, D" v/ e, J(defun not (x)
* ^" W# a; N, }% ~3 \6 Z1 B1 Q' {3 T (cond (x '())/ Y* M! o$ }* J( K' D7 R
('t 't)))
: I! a! E1 n% F1 e: h0 V. H0 P(defun append (x y)+ |- _1 e; ~+ s1 K) `
(cond ((null x) y)
- l, A D% \" v ('t (cons (car x) (append (cdr x) y))))) 6 [0 ?3 Q6 P2 e4 K+ A- ^
(defun pair (x y)
+ n* I5 R( w1 V/ ?6 o (cond ((and (null x) (null y)) '())
" p3 m$ ~$ k* N: T, I5 T* P& J7 N ((and (not (atom x)) (not (atom y)))
- D9 o0 [) z- O7 K4 `; m" m (cons (list (car x) (car y))
# V# ~8 i: }$ a6 a3 _" v4 \, |2 t# F (pair (cdr) (cdr y)))))) " [# ^7 l2 J/ s
(defun assoc (x y)( ^* ]: n8 | I6 t2 O8 }# o
(cond ((eq (caar y) x) (cadar y))
) b0 s" _( {7 k ('t (assoc x (cdr y)))))
/ U' s2 [9 F7 O0 T) m(defun subst (x y z)
" H( ]) J$ }6 _! B+ ? (cond ((atom z)( L; ]4 H9 m! a( s$ l2 B
(cond ((eq z y) x)
' M0 X! r: G2 F( _% G6 }7 U F ('t z)))4 l- a2 u* n* d7 J4 A% h# E
('t (cons (subst x y (car z))( a$ ]4 B8 l. C3 |+ r
(subst x y (cdr z))))))
; I4 e4 Z; @/ j如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
0 L, O+ C# z& I( k6 ~, {
f6 P/ S% B' f! {/ z# h这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。$ Z7 A [+ l+ C" o, l
7 y' @$ P! }/ G/ }0 _% Q# `. U
理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |