Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:
0 z+ _( T$ J- b: z# m. W
% B4 ^, w& l5 R$ s2 n+ U既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
4 P Y+ y2 Z2 n1 E- e在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
- _- y8 f* X7 G0 _! g(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
, ~; ?6 G/ K. n1 \6 B$ h
3 ]2 E' q1 ]) q; S+ e. N; l这一集中,我们将描述几个常用的函数,并给出它们的简单实现
. w1 \: P; I% f4 @3 z8 Z# i7 _
7 \8 i( |% s5 w% v# {首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
* }* T3 I) G+ B# w( q9 b, ~/ V& @$ A. _ } i9 J
可能有些读者已经想到了,取第二个元素可以采用如下形式:9 f, S' o7 w, @% G, e2 D5 \# @
- y# D# `5 T+ c6 H! u, B/ w% d
(car (cdr x))
" g' f( ^5 H4 d# K) `8 V, s4 `; s' E" r
同理,取第三个元素是这样的:/ E6 M% T4 t8 L! W
- ]) q& u; D9 @(car (cdr (cdr x)))
" |: j$ \; @/ F4 ]6 ^, O5 |
2 s5 ]- O! @1 f# z" S事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
! G& L$ O7 ^+ ~! L3 Z
1 |) Z0 Z: T' z$ n u& I) L# P> (cadr '((a b) (c d) e))4 F8 s% j: `; \* ^
(c d)4 B( E, |& Y1 n! R! _2 B- }
> (caddr '((a b) (c d) e))
: K7 s) M5 @( t3 s3 l* me
6 \! }' E/ G& @9 p4 u m) h [% Y> (cdar '((a b) (c d) e))
5 K! C" `* ~8 k" G9 f& X) \(b)2 `+ P4 U9 g& x- `: O
$ i- s; \; }/ w) ?6 O9 i% U. f
另外,使用(list e1 e2 ... en)来表示
! F) C5 E2 M: k( B8 T1 U# V' `(cons e1 (cons e2 (... (cons en '())...)))7 ^4 U, @( W# q) A% ~) H+ \
% g# G. H3 ?8 K- @/ [( O4 P> (cons 'a (cons 'b (cons 'c '())))
4 T& v. P( n& c(a b c). h! t6 o$ S7 N7 q% y4 k5 f
> (list 'a 'b 'c)4 _. C+ ~5 k& c2 t& n1 ?6 R3 z6 G
(a b c)
/ t2 o: X$ }6 F& K/ f! t& U# c. S) [+ b! e) q0 G
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
g1 ]* o: a) h( j, L, _) r9 s/ H* v1 f1 G* U9 S/ s
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)! J6 F F8 T* F
3 L1 M) C+ P& Q- Y. F(null x),测试x是否为空表。例如:1 q: V) c! i* }+ [. P2 y, s3 y* s
> (null 'a)/ }0 J4 ~# H5 |! d) x! Y+ r1 ]' t
()- D8 E. [' j1 |5 ?- [
> (null '())/ r0 y8 C8 G: w# h
t ; ^+ F% ^7 A2 s! `" t3 c
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。, ~4 n" Y6 f% G9 W3 A
> (and 'a 'b)
# z) c1 X+ E$ Y# Ct
( X- Q7 K5 m5 t* e, z2 q> (and (atom 'a) (eq 'b 'c))
* }6 T# e5 r9 x3 @5 [! o/ l& ]()
) r) o/ s. Y, z) Q(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:" J2 ]& U3 q- p( c
> (not 'a)
. S+ A2 U& Y* L3 V# x( h()
" Y8 p3 `( O# Z1 i6 r3 g> (not (eq 'a 'b))5 f8 y9 k3 ` P9 N" R
t ( c; p7 ^7 {, Y2 J
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
8 O" K2 J4 G% [9 A> (append '(a b) '(c d))
* ?( k6 {, L0 K# L' z(a b c d)5 ^1 o: J$ k1 ^% e+ n. n, J
> (append '() '(x y))
8 Y" q ]3 k6 e7 i1 E. G- b(x y)
- L1 T) c& C1 x(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:
( f/ N& n6 C. L0 |+ s' G> (pair '(a b c) '(x y z))( i1 E! q+ p8 g7 e" i; b" [; Z
((a x) (b y) (c z)) 0 W+ d* T% v4 t% j8 x o
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:4 t1 A% }3 {* w( K4 f! _1 Y
> (assoc 'a '((a x) (b y)))$ E+ Q% l" ]* n& ~- ?5 V+ K
x. J% V: ^; t$ d
> (assoc 'a '((a (foo bar)) (b y) (c z)))
8 n4 X, {5 t& S+ t& z(foo bar) # Y% @5 X+ S" v! N
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:+ i. i# k! J0 a9 {1 _! |2 z
> (subst '(x y) 'b '(a b (a b c) d))6 t7 j1 h( P1 c, \! z+ \) M4 D
(a (x y) (a (x y) c) d)
1 T# l) Y: a. P3 i: d7 e下面我们给出这些常用函数的简单实现:( ~$ K7 _/ I# p9 ^5 A0 G* R% Y, G3 o
8 ^* v9 a! J0 Y- l* w
(defun null (x)3 C4 K8 C- B8 Y
(eq x '()))
2 [( J! ]8 U0 c" P1 G; x! u(defun and (x y)
: Y0 J* e4 t' U (cond (x (cond (y 't) ('t '())))
# k& }$ @- B A3 i+ x4 h ('t '()))) 6 O" l! N D5 y2 b. t5 w
(defun not (x)0 w# u( F6 R. X% N7 ^$ t
(cond (x '())* w5 P8 P4 C7 v+ b
('t 't)))
% ]) h' @# V2 U2 O+ |2 n(defun append (x y)9 D& M! P- d8 r2 y3 t
(cond ((null x) y)
# r/ b7 G K* a( x7 d& t ('t (cons (car x) (append (cdr x) y)))))
6 I C9 O- s |) y, D% G(defun pair (x y)
6 M; k; m& G( }9 Z+ L (cond ((and (null x) (null y)) '())+ g. X/ o0 L: U8 I$ R& c
((and (not (atom x)) (not (atom y)))1 |/ r% q; c; p. d9 O
(cons (list (car x) (car y))2 d6 z$ C6 l4 t: j3 X( s0 [
(pair (cdr) (cdr y))))))
e8 b- u' i7 y8 _(defun assoc (x y)
- G" a- f/ _8 _3 f) F8 e (cond ((eq (caar y) x) (cadar y))1 p, V2 J! L& ^5 j2 y, c- M4 b
('t (assoc x (cdr y)))))
5 t. |% |, c7 N2 I7 a [(defun subst (x y z)- K+ r$ a9 U8 Y( S$ R
(cond ((atom z)* X6 @9 G0 G" A6 d8 ?& D# G9 W
(cond ((eq z y) x)
. q* O9 J# q' Z7 B, L: p3 v, _ ('t z)))
6 O9 Y& Z. L7 a2 P$ E& o9 k2 L ('t (cons (subst x y (car z))4 \5 h, C6 `# }; ~
(subst x y (cdr z))))))- Q! v% M5 D7 @1 b [
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。) j/ X/ R6 J1 D) S6 S$ F
% ?/ ~; U( r2 _, v! V4 Z9 ?
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。
8 |6 n' H. w" |! b3 J, ?5 h0 X* Z% e$ v6 B! t( {" N
理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |