Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:
9 p9 }$ m# w; ]" X' ^6 g, p3 H( E% Z
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? 9 Q+ J" _* P* m
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?! I3 d" n- m- y( j8 a# X
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)5 Y9 a! z1 Y# ^8 w% r- A
* c/ R; P5 Z Z; P- F/ k/ \这一集中,我们将描述几个常用的函数,并给出它们的简单实现- N9 ]" Q) s$ L1 U6 |
9 I+ a j" G/ a0 v+ Z首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?4 R8 @/ N; Y: U, {4 o$ l8 S+ C( }( ?
. J7 L' ^8 y. U9 v可能有些读者已经想到了,取第二个元素可以采用如下形式:6 \4 {2 `4 X# `! O2 W: t% E# F
' H- D: o% O+ `) ^4 m(car (cdr x))" m, z4 ]* r) R; V. l$ w
$ p8 \0 R0 M2 R) A' |
同理,取第三个元素是这样的:6 Y9 m3 a0 V6 g
3 L8 ~" ~8 p4 D8 ]
(car (cdr (cdr x)))
& r9 m# p' Q- q9 t! S: `) A: e! _, J' _; }* |5 f/ v
事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:$ t& `3 j; w. J& D
6 {/ ]. |1 `) T& f> (cadr '((a b) (c d) e))$ X/ I L& |0 o3 |4 _9 v
(c d)+ _" x# p+ @& U ?2 A) V
> (caddr '((a b) (c d) e))1 h7 q6 P9 ]8 `: _- `4 o: d
e
" j' C2 g& h* b% G9 }: X> (cdar '((a b) (c d) e))1 G. q$ C, b W7 N3 g$ |: \
(b)
/ d' I1 a4 z' |+ g7 D* |0 v
1 F) q1 T9 y3 Z5 } I; y另外,使用(list e1 e2 ... en)来表示" x7 E5 ~7 r" i1 X/ Y
(cons e1 (cons e2 (... (cons en '())...)))
9 z! S+ }( z" u% j1 m$ u7 n' y: N$ A3 q F X# I3 ?7 ~
> (cons 'a (cons 'b (cons 'c '())))
4 S( k- s0 H8 [$ [# Z& f7 G+ ~! f6 k, o(a b c)
5 t' d5 I+ J1 N" {: j+ d> (list 'a 'b 'c)
0 B! O$ C* u& c- j(a b c)2 n% l/ o) u& ?0 \1 k" M# h4 y
0 D& w0 j) K* {% J* ^ h; N, n现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。/ Q' P# z# v$ j" X2 O* t
f, i. g5 R* i& y" z6 G& k2 Z
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)0 F9 ?9 e( U/ o. b: N6 M
: f/ X, E8 q) U( P
(null x),测试x是否为空表。例如:
( ]# j# m+ _; H& _# u, W+ l/ }7 _" A> (null 'a)
4 j2 Z8 }0 C% e3 V4 U# @()1 B1 ?5 v* h7 ]( m' @
> (null '())) _, w; f2 Q- b/ u' g8 v" v
t
4 U/ y9 ]3 N6 V3 P) B: p9 ~(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
$ ?8 q |* J/ b& R" u# ^> (and 'a 'b)
; I6 I5 `4 u% B$ R W9 `t8 b1 U8 j! K4 X; _- e
> (and (atom 'a) (eq 'b 'c))2 h$ ?! w1 \6 |# j( N4 ]* k- t
() ! B9 u/ O2 P f, X0 [" f
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:# ~) E [0 `4 c9 ]- r3 w3 _
> (not 'a)
% m8 Z: d- F, O! M, N()
0 u) `7 u* L* [/ \* U! q: d- p> (not (eq 'a 'b))
% t7 D. r, r9 v: _ Lt ' P9 P/ W3 a& P" i, J
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:: F# o# e0 m2 o2 q5 U( b2 j
> (append '(a b) '(c d))
" @2 |9 M2 U; U* V$ w% q(a b c d)/ l# l3 D" R7 q3 [+ U. l) S
> (append '() '(x y))
8 o7 _* P9 ]3 \+ \. z: |& E(x y)
o8 K& p. v( C4 ~8 E* i0 `(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如: J( Y' L5 Y$ o+ y3 J, U, d
> (pair '(a b c) '(x y z))
6 ]% y1 O( I7 p5 i# k9 n((a x) (b y) (c z)) , ~' m0 H V- x& M* U: A
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:9 M* {) ]# H: g
> (assoc 'a '((a x) (b y)))
! ]3 Y3 G, k' S" @, D+ X" }/ ?x
4 E! b: F* ~% H> (assoc 'a '((a (foo bar)) (b y) (c z))); h, y7 J5 X( D' R
(foo bar) / C5 H5 { @. d6 E K
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:
i; U5 Y u+ L' Q; R- {% W> (subst '(x y) 'b '(a b (a b c) d))# s" x8 n* f* s) ]5 @
(a (x y) (a (x y) c) d)
! S0 U1 s/ {/ F下面我们给出这些常用函数的简单实现:
$ a' a4 r; M2 N$ j M C
8 V( ~3 }- r9 } }8 p(defun null (x)
0 g* @1 e8 }" ^4 w, v0 H" Z (eq x '())) " a* ^3 ~& h1 M; E I$ j/ P2 q; B
(defun and (x y)
# b6 F5 P9 ^/ L, t1 r' p# { (cond (x (cond (y 't) ('t '())))
! c5 N6 F0 L$ s& ^ ('t '())))
1 G6 b- K5 |. u: l(defun not (x)7 p4 i! d4 I D& R
(cond (x '())% r1 K2 L* T: J' P, P( f; f* {$ x
('t 't))) & X, p( M( w/ Z1 ?" E7 E. O
(defun append (x y)$ r+ `9 t1 m' R& s% _4 r" c: ]: V
(cond ((null x) y)1 H# n2 d+ f6 k+ X
('t (cons (car x) (append (cdr x) y)))))
* X, u# H! @) ]1 k(defun pair (x y)
4 t# [2 H8 d+ ` (cond ((and (null x) (null y)) '())
* t' p+ W" z4 N$ t& H# K+ Y ((and (not (atom x)) (not (atom y)))
( N. s, h* U9 | (cons (list (car x) (car y))
$ X, k0 f9 K# c# s$ j (pair (cdr) (cdr y)))))) . B. u! ]" \3 J7 y( D
(defun assoc (x y)
- ]9 M1 F9 _. O8 B (cond ((eq (caar y) x) (cadar y))2 R$ ?! w7 H1 `* ]. o N, Z4 H
('t (assoc x (cdr y))))) $ h# d3 z5 |+ j7 H" Y) T; o3 P
(defun subst (x y z)
& t0 g4 r7 Q0 P9 h4 m" X2 d q (cond ((atom z)
. X+ A# m- g- T2 V% \$ { (cond ((eq z y) x), h' Q; @5 ?0 v
('t z)))8 l$ E. X0 n3 p' S9 h
('t (cons (subst x y (car z))
% e# X5 ?( }1 O# q7 u/ Q1 a (subst x y (cdr z))))))
6 O! B/ a: g! F0 }0 M H如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
3 e% A# J# e$ d3 c" c* H9 J+ H. D& l9 v! G6 U
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。2 y h; G: X6 ^4 o) y9 d# x3 w7 O
8 V: }1 r9 \# x) B# |6 J: Z理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |