Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:4 N( E4 d8 \( T: e* D
j( p, h( W" ]7 t" C
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
+ R& M( [6 L7 A+ s% h在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?* |' M- h6 {( j, q7 n# A r7 ?
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
- E: R0 X, i1 E: r' d
2 _- |* d4 a- t6 m这一集中,我们将描述几个常用的函数,并给出它们的简单实现
; y5 ^! o3 |6 J! ~% J
5 b' M* N* z$ L" r2 |首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?8 h' W0 t& l# L
- q+ h. |$ P! h0 B- F5 ?0 L可能有些读者已经想到了,取第二个元素可以采用如下形式:
& I; u; | y2 G3 Z( d5 @7 K) @. L2 }$ |4 K3 C8 T0 d
(car (cdr x))4 [# n8 f* X- _/ b, C( O7 ]; T6 C
b5 f! |" @# m
同理,取第三个元素是这样的:! C; i" o" D8 b0 x( [& o
. R+ y- Z+ t7 W(car (cdr (cdr x))); p3 J6 N( N* Y( l
?- C1 D( d1 {$ L4 N" l事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:/ q% n7 q( h2 Y5 v. M2 D4 q
. e# y& h' f( D$ H4 c+ x> (cadr '((a b) (c d) e))
) F) r- ?) @- A/ @% o( x(c d)2 D6 Z5 q+ z/ K5 t* a, O
> (caddr '((a b) (c d) e))
5 {" p% ^5 k& W! c' R8 ~, y, B2 ]% be
( P: g1 F4 U/ e5 X/ ^5 \- y. H> (cdar '((a b) (c d) e))
) |, u# J/ M7 H4 L, ?$ a(b)
8 v* i1 z8 t% E7 C* H) _8 o+ ]) h3 y, d/ f& M. G f
另外,使用(list e1 e2 ... en)来表示. l' c Q# k/ i G
(cons e1 (cons e2 (... (cons en '())...)))2 \ t/ t9 ~+ X; |" q% L9 S
8 W4 C, W! G2 m. }$ ~& N I> (cons 'a (cons 'b (cons 'c '())))
4 Y* e5 Y: B! X: H3 L(a b c)# z* C7 T4 A2 s# g# W$ J" f: R
> (list 'a 'b 'c)
4 ~+ h( ?# d! v/ E8 f0 G, T(a b c)
' f, v1 G3 s+ o3 ~- g
1 ^, m* |7 J8 [- U5 s" S& v. h$ y$ d现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。2 z) c! k* f- Q' m" }& a1 a. D
* q4 s' _( a" N* |& ^ L" q3 y
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)
" d+ f, ~; h; E1 b& ~ G
$ z* h9 r6 m; U& w(null x),测试x是否为空表。例如: p* y; M% y- Q- D7 r) S3 T2 O
> (null 'a)1 @8 I4 f) _6 s; D4 Y, w% N
()
' |" ~% B" {& i1 X" g {7 I> (null '())
5 N* c% f: z1 D6 c! A/ M$ }* }; [t 5 p: F0 w1 W! _1 Y
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
G# ^* C9 f2 u7 p" k& h$ ~> (and 'a 'b)
: S4 {% L4 G' p. n% kt |% f) v# G3 x) A
> (and (atom 'a) (eq 'b 'c)) H# [% V' p5 ? [2 x" v$ P. k
() + P- p& @( p9 Q; m+ c6 U
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:
3 j5 \; v: ^' T0 v* b> (not 'a)
! `, [- g5 T, `' }$ v()2 [) d, e) w- l) W2 G/ Y9 {/ L0 l
> (not (eq 'a 'b))
% A9 A0 g4 ^1 S8 @( p8 ~t
0 J+ f9 I5 k' {" e# A6 ](append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
' e" w( F% i; a& h$ u> (append '(a b) '(c d))
o7 k' I+ o R3 e8 @( p% o: ^(a b c d)( A% o% ?" h% Z
> (append '() '(x y))) V" N1 L% {* G
(x y) 1 D0 P4 H2 S+ V" @' q& `
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:/ h9 B# o# S6 o
> (pair '(a b c) '(x y z))
# _$ y4 m) \. m4 i((a x) (b y) (c z))
2 v) H$ c3 L6 W$ U5 Y(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:' G' V2 d: X7 g- [0 M
> (assoc 'a '((a x) (b y)))
5 x p* F& }' Q! wx
+ \& Q& \7 C9 S# E$ O% A9 C> (assoc 'a '((a (foo bar)) (b y) (c z)))# r) a/ a' J" R/ p, D
(foo bar)
/ A- F" K3 l% x! N$ L4 a+ R4 [(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:- j6 {; F% j H* Q( ?
> (subst '(x y) 'b '(a b (a b c) d))
# K) s! L4 y0 m* k: p5 d& p(a (x y) (a (x y) c) d)
- ] }) ?( R. | }! E1 h% p& o下面我们给出这些常用函数的简单实现:& d4 k' N& M5 o" S3 E
+ k! E$ Z8 z- [8 y. @
(defun null (x)
9 q( R5 Y: H- G; P; U (eq x '()))
+ _& }. y: ^& @6 S2 a(defun and (x y)3 Y! F3 X: d( S' h' I1 C& s) a
(cond (x (cond (y 't) ('t '())))
, Z0 _* @+ o" V+ W ('t '())))
& B3 g% Y7 V) Q$ c$ B(defun not (x)
* ]# ~4 \4 Q+ ^ (cond (x '())
, G+ B0 {1 Y$ Q+ _+ S ('t 't))) - ?' j5 c& u% d( ^: k
(defun append (x y)
" W' m, h1 L7 \9 R( ^, E8 X% v (cond ((null x) y)
: c4 b5 |, R) e ('t (cons (car x) (append (cdr x) y))))) 1 L9 X0 x# N g( u7 ?; V) P0 Y: n
(defun pair (x y)
! n1 W3 Z, ]0 I+ ]" s (cond ((and (null x) (null y)) '())
9 G4 @+ i8 n3 a. u( D ((and (not (atom x)) (not (atom y))), e, z. K2 @! i I5 N
(cons (list (car x) (car y))% V( Y8 m8 Q9 V( W7 r( g2 Z/ }
(pair (cdr) (cdr y)))))) 3 G2 ?! P6 w2 f1 s
(defun assoc (x y)
% u$ u, B T' G9 L (cond ((eq (caar y) x) (cadar y))
; O4 x6 b! Z" ? ('t (assoc x (cdr y))))) 2 i. i* |) L6 S8 k7 w2 C- G
(defun subst (x y z)
' X8 f- k' a: R1 |7 H- J (cond ((atom z)# N8 p# B/ v i
(cond ((eq z y) x)
" x$ ]( ^& T& i, `# J3 T ('t z))): F0 ~# x# f2 g$ y
('t (cons (subst x y (car z))9 C7 h- [; }0 Z1 N u, \% b9 W, P
(subst x y (cdr z))))))" E* P' u; M8 C H* l6 r0 ]
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。7 d/ j: b$ N7 x& ]8 w8 r5 c4 V
( M% y0 F3 L j5 b i8 k
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。- @4 ]3 I3 j* l
7 j; ]- l, t5 L4 x: t- E- S$ `理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |