Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:% e1 W! |4 x) c1 y& \4 u
! w, c4 n* d. G
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
, q6 o- ~4 q1 q. _- R在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
0 M+ g/ p( m! P1 ?+ I6 q( R(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
$ F' c$ U: ^0 M6 ]1 _- k/ l1 _
, I- b( e( S) r0 B1 u1 A这一集中,我们将描述几个常用的函数,并给出它们的简单实现
7 B7 G$ s! P$ H) v& q9 X+ ~
) q; F1 E7 v1 J0 l首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?: \, ?& i6 J$ P0 F
, }% x& ^. Q0 V+ P可能有些读者已经想到了,取第二个元素可以采用如下形式:3 S* S% L' Y" t7 ~) F
8 m! i" \ v8 m6 a) O. Q8 |5 f(car (cdr x))
3 q7 ~8 a' t/ X# N7 q* K
2 O+ h/ M- b- P9 u0 ?1 I同理,取第三个元素是这样的:5 Z9 b; `- C H: n6 i3 ]
8 V# [' O8 _0 z# w: i9 r7 q(car (cdr (cdr x)))
9 l2 L( X0 o- }, M* r! \1 q3 W/ u$ Y0 s3 K/ g q1 I( I: O
事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
+ Z8 v1 b' U& Q- [: u' ?) @" ?
+ L6 t+ @( }+ c# B- ?/ z> (cadr '((a b) (c d) e))
( B% c1 b; ~- l# A$ g4 A(c d)! ]" ^, [* r! c: G, C
> (caddr '((a b) (c d) e))3 b6 |& X, N) _ K7 e' b3 F
e- m n9 g1 e% J' m* j
> (cdar '((a b) (c d) e))
" \( Z$ C8 O' f1 V, L4 s+ l+ l. e4 M(b)
7 C, I6 n- P0 o% [, v u
2 A+ r' k+ Q/ e# s3 F! z" ~另外,使用(list e1 e2 ... en)来表示
& b, |+ v& g! N1 H: j(cons e1 (cons e2 (... (cons en '())...))); C( }$ ?9 f1 A9 i
! X7 T9 H7 B3 m! X
> (cons 'a (cons 'b (cons 'c '())))
, c* n, ]5 W& l. ](a b c)% L( u: I) k) {; r( F- o
> (list 'a 'b 'c)- g( n2 }" q1 v' E8 u/ q9 k0 z, t: e0 \7 K
(a b c) i3 ]; |$ @) ^
+ a- g* F3 O( ~5 A/ \- m
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
& r& i/ j* @% i& V/ I+ ]2 n. K2 o& k
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)- n# W) Q& w+ ?5 N+ X8 J
. {5 G. P. R! ~8 A) I(null x),测试x是否为空表。例如:/ d0 z6 Z' v) o5 d
> (null 'a)
# k" E: n. K$ V$ O9 |4 Y* U()
. Z( U6 h0 u9 W> (null '())" y9 i1 c! p# ~ K; X
t
7 Q7 ^! y0 w4 ?1 S/ ]! s g2 C; c(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。$ M0 C! `3 z6 L( k+ r1 R. U7 p
> (and 'a 'b)
0 t" L B3 [7 D5 @" \% `. ft4 i4 l* b! o! r! l. i# x
> (and (atom 'a) (eq 'b 'c)): `% m8 M. b. N
() 9 E7 V8 W- _' o; l8 y7 i8 f9 K
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:
$ `; P* p3 M2 {> (not 'a)/ x/ Z; l- g# {+ i9 r! W
()0 I" z) I2 `4 g2 u9 K, c# O
> (not (eq 'a 'b))6 V2 R: b. Z {, }, C& L
t - f" z! P6 ~ I. q* n7 Y2 T
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:3 `/ s8 z# N: I5 g- v. o& j
> (append '(a b) '(c d))8 m C" g) @. F/ U
(a b c d)" [; o t. c( J; w) N. |0 i
> (append '() '(x y))
0 u4 K5 s1 l0 M9 e: C(x y) ) N' j$ @/ q* W9 V' l- m; ?) C O
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:0 U+ J7 D& M' g: @+ [& x2 W. `
> (pair '(a b c) '(x y z)) H2 ?9 w) d L9 }% R9 w
((a x) (b y) (c z))
, v, }" G% x, V(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:0 J8 j" X/ M6 b: \& N
> (assoc 'a '((a x) (b y)))0 ?( C. m8 a! r f: y
x& S4 V% O9 t% o; @
> (assoc 'a '((a (foo bar)) (b y) (c z)))7 m5 k Z. w# V" H, e
(foo bar) * d2 m) ^/ r; S8 |
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:% C. u$ d' H; t) {* f4 b) K
> (subst '(x y) 'b '(a b (a b c) d))$ J4 {5 `1 ? _0 B! r( m8 o2 x; K
(a (x y) (a (x y) c) d)
9 K, b: g! b" @+ I' F7 B1 \1 H3 P下面我们给出这些常用函数的简单实现:
: _& S- J; f2 ^2 G. n9 ^
" U Y5 v n* r7 r9 q(defun null (x)9 E; \* f; n" l$ U; k; _
(eq x '()))
: Y4 G5 R, V0 K: Z6 m& A(defun and (x y)
( |; M1 k- i+ `$ v8 o7 b$ W (cond (x (cond (y 't) ('t '())))
. ]8 i/ f* U) z# E" ~% N% f ('t '()))) 6 ?( t; G$ w. J: w, D& u \
(defun not (x)
2 z- o# L$ U, h1 ~1 H9 | (cond (x '())
0 H. R& i4 c2 f# q ('t 't))) 2 u- s# [+ c+ z. i* X+ d
(defun append (x y)
( K7 [1 W2 B$ @, E9 ^2 g (cond ((null x) y)
3 b- M6 x2 O3 l ('t (cons (car x) (append (cdr x) y))))) $ Z. V! q* C! i
(defun pair (x y)
. N3 s% }/ q6 l7 t: E) V* X (cond ((and (null x) (null y)) '())
! D. b4 Z7 p2 Y* F% L' e2 \ ((and (not (atom x)) (not (atom y)))
: J& x/ v) J- c( H; ~ (cons (list (car x) (car y))' F: b4 `+ U- I m
(pair (cdr) (cdr y))))))
" D7 \* g, d% L, `( N(defun assoc (x y)/ s% z3 Z0 \9 ^! }9 `. |
(cond ((eq (caar y) x) (cadar y)): {' w( T/ P5 v& G3 T" F
('t (assoc x (cdr y)))))
; M, S) A7 H% @* n3 t(defun subst (x y z)6 E0 @ s$ X: J
(cond ((atom z)( I+ b2 S# f# Q* s7 K* w
(cond ((eq z y) x)
' i7 J: h5 `6 Y# O! t ('t z)))
+ B/ {; @/ M# d4 d3 ?4 Y ('t (cons (subst x y (car z))
9 a2 j' f6 N4 Z* i% ]7 a (subst x y (cdr z))))))- C E6 V/ r1 m/ D Z. Q( X# w
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
' X# [) X2 I$ G8 s; V. c# i9 M% O, t# K: Q ?: v/ d
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。
. t' i0 h9 S" n& K2 E- k+ [" f. ^# E0 F; C( x! h
理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |