Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:# K9 r _' N& L( t/ ?' d" X# t/ }& h
1 V+ M* Y/ Z; r+ K9 H* C既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? & S, o/ }) T" Q) a( b
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
, x" k. D: t9 L! y) r/ |(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)5 z& e9 d; K( j5 ?$ ^
% ^& X' H% b: Z这一集中,我们将描述几个常用的函数,并给出它们的简单实现
! l6 X$ w3 }% S' \1 T% {! K$ _ u% j3 n) e# B+ B
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?9 T# W1 c2 H" F) \( O9 D
$ x E5 [2 |3 _2 e* w可能有些读者已经想到了,取第二个元素可以采用如下形式:
D3 ^3 F7 F* p/ j, I" h- J; D, y6 n3 ~0 n" M6 u) i4 o+ E
(car (cdr x))
6 w( {! Q6 j; c/ T! N. i7 i8 Z
0 C* z5 u1 f; ? @7 S" ]- `7 ~0 T同理,取第三个元素是这样的:" Y! s9 B0 _7 T* M4 |! |
/ Q9 c; P& P1 R, f(car (cdr (cdr x)))1 a3 N* [* j0 J$ Q" P. I! j
1 F$ D" z* K. Q$ d% m+ p
事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
- k% l; V A6 ~( _) Z/ }8 t/ [0 @9 ~" N) B5 }( S3 A: I {
> (cadr '((a b) (c d) e)) n8 j0 d! v& t3 J
(c d)
, W* I9 H% [8 \> (caddr '((a b) (c d) e))
4 V* @4 F9 y7 }e: \8 e( ]' u' d' r2 L
> (cdar '((a b) (c d) e))/ h+ J$ G" ] n$ ^. _+ x! x) B
(b)5 \ F7 b1 Q; K% G( ~5 i
, K/ k. u" {9 n另外,使用(list e1 e2 ... en)来表示
7 ]/ e! B ^. ^* H6 ? A+ v, I1 z(cons e1 (cons e2 (... (cons en '())...)))
# ^1 `+ m. M, c+ j/ b+ e1 O8 B0 F5 W( b0 N2 k
> (cons 'a (cons 'b (cons 'c '())))0 i! R0 G8 v% A( g( }/ D2 y0 l8 b
(a b c)
2 e% N& I( b# O/ b7 ?8 V N$ U> (list 'a 'b 'c)
: Z5 @/ J$ Z& R# U(a b c)5 y! j, F. Y+ F4 T# U
. K# x; W% A" M- a' G% Y) S现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
$ h0 Z( I6 |$ @" Y. U S# M* z' W+ g. O/ V' |/ V
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)
5 S4 Y. s6 d, S2 W4 R' T1 b+ A' N% b! H- y! b# r
(null x),测试x是否为空表。例如:5 z0 `1 O7 j3 E, Y. ]
> (null 'a)! p8 x8 p- f( q; H7 L, J
()
! L& K7 S M8 v. y" P V> (null '())
) F! N/ o H, I J! Mt + l' T7 b- \/ G$ j7 t0 T- ^
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。/ ~- o% S( R3 Q$ i2 F
> (and 'a 'b) ?2 n* n ?* z: v
t$ x; u) t4 V' N& h7 W, C# _! f( G
> (and (atom 'a) (eq 'b 'c))- t9 a, k6 j. \0 P- C3 w: j% p
() 2 X: K; A9 ~7 `; u
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:1 I) \6 ^9 l" G, K4 J! T/ }0 U
> (not 'a)& R5 E0 ]; H8 ~5 J/ E( k
()7 F& ^5 Q( H4 i5 X% }1 m
> (not (eq 'a 'b))# s% ]8 ]" @( Z' J8 B& U
t 3 s5 \" s* R2 {. r
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
8 }+ t3 J* R" b, J1 C" c" i6 o8 ]; t" Y> (append '(a b) '(c d))
4 V# ?. r- f/ v8 v% d(a b c d): [4 }5 \9 z& Q ~; \
> (append '() '(x y))
6 [9 n4 A. l2 y- P4 N( \6 ~(x y)
) A! f8 z7 q1 K3 x(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:( O9 Z3 l8 |! B4 Q# ^, b
> (pair '(a b c) '(x y z))4 I7 Y$ R, j; ]1 V5 P: Y* N& a
((a x) (b y) (c z)) 2 c4 _' K6 m9 }
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:
W7 Z5 H3 X# T> (assoc 'a '((a x) (b y)))( B5 I+ f: S4 K4 B0 M& u) ?
x/ m, j8 M: Y1 Z2 i' m' C4 R8 p
> (assoc 'a '((a (foo bar)) (b y) (c z)))3 |( v4 V; @) w1 s8 Z, Q
(foo bar)
& Q- x) X9 T: C) Z(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:- b8 _( {% a g& b2 J; ] q- n0 V2 i
> (subst '(x y) 'b '(a b (a b c) d))
) w8 }7 {7 t, u/ u(a (x y) (a (x y) c) d)
7 A2 s$ ?1 A/ d! s+ b' A1 c下面我们给出这些常用函数的简单实现:& \3 E% w) n U( S0 {6 m/ |
/ h& `: Y# ?" w(defun null (x)3 j% \, H- {7 C; R
(eq x '())) 2 o) T* C3 u; a$ e/ ?* ^' r3 x% j
(defun and (x y)
# n" \+ W6 G* L; L) b (cond (x (cond (y 't) ('t '())))
, N4 z* [7 y4 P3 p' t ('t '()))) - X7 r3 K. _5 E( A
(defun not (x)" H& O. Y8 d* g
(cond (x '())# Z: S: h" v: R- d3 ?7 J5 }7 ?
('t 't))) # |$ t( M( E4 Q$ a* a9 L4 i
(defun append (x y)
& Y, [* ?0 h$ \+ C8 `% X6 } (cond ((null x) y)
y$ Z$ ?3 g& b. p2 f3 Q! E ('t (cons (car x) (append (cdr x) y))))) # _% f. z7 A9 q; _9 @+ _/ T u6 F
(defun pair (x y)2 Q0 E( G9 ?8 K4 b. w; K! S0 ]- {
(cond ((and (null x) (null y)) '())1 v: e: B4 N, S( m
((and (not (atom x)) (not (atom y)))/ y7 f9 ?% L$ Y3 a
(cons (list (car x) (car y))
% d8 W5 S3 c( C# Y& W. F (pair (cdr) (cdr y)))))) / @, j; c3 ~1 a* s0 C: v1 W
(defun assoc (x y)
8 z: ~/ p! G5 g: e- N! v5 _2 } (cond ((eq (caar y) x) (cadar y))* E0 d9 a5 U9 `1 g3 b& `6 u4 n9 l
('t (assoc x (cdr y)))))
7 l4 u1 _. `+ A9 s; l- m" ](defun subst (x y z)
$ B% ]/ R, K6 h2 ~ (cond ((atom z)
4 l9 w& H% Q( Y/ l4 Z (cond ((eq z y) x)# Z9 W/ _7 T7 h
('t z)))
) N# C' R* o7 |6 c4 e& e ('t (cons (subst x y (car z))
8 e" g5 \! O/ V2 f. p+ }$ S (subst x y (cdr z))))))6 k$ {/ w& s. H4 O
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
7 ~/ R* I0 I( s( B- t- `. {
, F! K& ]: K, a' f2 q" x这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。9 m' S5 E0 Z! U# c- s0 y6 U
: D1 J* \/ q6 ^; |" ?理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |