Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:
7 d/ d9 H; C+ x1 [( j. A; L( w1 ^9 G/ A1 R
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
/ J5 M3 ?6 J# U& F$ g在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?% S- @9 P) i+ w% O) M
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
$ I6 B9 f/ d5 {2 b. S7 H# W6 B" J* ~: o6 g
这一集中,我们将描述几个常用的函数,并给出它们的简单实现
! a5 ^6 ?- B" ]9 S8 X
; D4 [& I1 [- _: J' B0 r0 Y首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?! W/ R: s, U: A3 l$ t& F' P
+ W0 ?$ V5 b' J/ _+ E2 W" o- ?1 R可能有些读者已经想到了,取第二个元素可以采用如下形式:
) V" e/ k! R$ P( i& v3 s* G& E$ Z9 a* y$ N
(car (cdr x))2 Y! e+ a n- F$ G
" O8 c- g& K6 s2 p同理,取第三个元素是这样的:
' f3 Y0 n) K4 H% m [5 D3 s+ J
2 N1 ^7 H! O) w; [5 l/ L) U(car (cdr (cdr x))), F4 o6 S2 H9 ]) u. E w
$ z/ j3 L0 m4 f3 y& A
事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
( ?4 K! q0 U4 o( _7 T" c" F( \/ _3 {9 d$ ~6 p& M: e
> (cadr '((a b) (c d) e))
$ X3 n" E, f: J(c d)
) a& _3 G9 o7 K" |* v> (caddr '((a b) (c d) e))
! p- j- Z6 ]' @1 s$ {e7 W3 O2 V" g5 I) [8 N) H& H
> (cdar '((a b) (c d) e))
( c. U5 W$ W- X1 k(b)$ H, t M* ^5 b r8 u1 T* R* H
4 F7 w$ y/ X0 R+ l7 ~
另外,使用(list e1 e2 ... en)来表示5 Z, S5 K, h2 S* C
(cons e1 (cons e2 (... (cons en '())...)))
2 W4 Y* S, C/ ?7 f. H0 }! _4 F3 h
- `' G( F) J0 S7 k9 [. n4 Y> (cons 'a (cons 'b (cons 'c '()))). ?: Q5 X9 ~+ n' r) |5 Q- x
(a b c)
5 h8 v) A8 ?& C; _" @> (list 'a 'b 'c)
, M/ p5 k- t4 T(a b c)
6 t) E6 O7 s/ o) F' T- Z- Z* e- |& q6 H2 q; n
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
- w; S1 n5 N; k0 O" j4 L: p a5 G2 `* P" A6 U0 ?8 o% |8 P, ?( Q
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)3 Q% P( ?3 _- L) z4 M$ ]. w
; z1 _8 I( T* z3 m) e6 h6 f% t(null x),测试x是否为空表。例如:
: y7 |* f' n0 F: t: T> (null 'a)
+ `. Q5 K" N3 \8 E" a( D()3 Z0 b0 l2 m" |# x
> (null '()); J, x% H1 l* Z' V9 ^" r6 W5 v
t
, D$ p. U9 Q; I0 P(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
# k( t& e4 a X% M/ N# s# ]% l O> (and 'a 'b)1 v9 ^. b3 J8 {7 L
t
* `. s- M; W2 ]$ a' m) w. f> (and (atom 'a) (eq 'b 'c))
/ v! _7 w. B8 r4 S+ V()
- d. a; J, ?9 q5 m. Y(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:2 M x$ u' a# O& V; C v% e" ]9 N
> (not 'a)/ q+ T$ l; k( ~* L9 g: K R7 x
()
3 Q* e7 s) W: _0 G2 e. `! _8 C2 `> (not (eq 'a 'b))
+ W y2 \5 A: O! H ~, Lt
* g" X& i- c' ?/ U! m4 ~(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:: |9 S7 y7 D/ \8 M1 B
> (append '(a b) '(c d))
; X! D6 e6 L3 A- R4 J0 d(a b c d)* G! `- H; U; l' K' @
> (append '() '(x y))
Y/ {& v* K; }& g(x y)
9 D6 U- e% ]5 O. |! S" m(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:! M. \* c: T9 r/ q9 I
> (pair '(a b c) '(x y z))# X- ]5 @" f" P! j- X
((a x) (b y) (c z))
& o" z/ t$ v# b" a% u, J(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:/ T2 F$ t# V+ {1 r1 c3 p
> (assoc 'a '((a x) (b y)))" L" A& l4 L( i. J
x5 r( `9 ]5 p' Y; u. _9 T, V
> (assoc 'a '((a (foo bar)) (b y) (c z)))
6 z0 ?6 O" ?8 v# |(foo bar) " i, [) o1 |0 u6 \+ @$ f& q" _
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:
, K8 b8 ^+ \4 @, x# L> (subst '(x y) 'b '(a b (a b c) d))0 z" \2 F: E. b- a
(a (x y) (a (x y) c) d)7 t" Y( r4 |, u0 N# L9 G
下面我们给出这些常用函数的简单实现:
) [- U; i1 P4 M/ r/ ?% b: {4 P
4 g0 f& ]9 L/ ]8 Z. h* c(defun null (x)1 W: a Z9 T& m, A, K
(eq x '()))
, q' a! k9 J) S3 v6 v(defun and (x y)
8 @) f3 n6 I' H/ q (cond (x (cond (y 't) ('t '())))
$ G- E5 \" g" J/ G& w: L ('t '()))) 6 C" s& g1 Q6 P) o, X" E
(defun not (x)) G* M% I4 Q2 ^5 L, { f
(cond (x '())
* s& r/ c+ \; R w$ g x) U ('t 't)))
8 M# R6 c1 ~6 p. F6 V0 W(defun append (x y)
. t% R4 Z1 c. K. r1 y; D (cond ((null x) y)# e. J5 m! w1 ]% c4 [, N
('t (cons (car x) (append (cdr x) y))))) * x# s" @" o1 l
(defun pair (x y)
( m$ H" V6 _- R (cond ((and (null x) (null y)) '())
; k- o3 \6 r, s: e' \! k ((and (not (atom x)) (not (atom y)))2 o' Q, |" m$ q# P: c
(cons (list (car x) (car y))2 g e( a, o1 S- V) b# P
(pair (cdr) (cdr y))))))
% |! x% t) z/ |& r: I8 m. q% ](defun assoc (x y)
* o& b* [4 q& J6 ~4 h" S" N (cond ((eq (caar y) x) (cadar y))6 p; h3 i2 @. m: v1 z( @+ V* e3 Z
('t (assoc x (cdr y)))))
4 X/ I. r: C. d& k(defun subst (x y z)
4 E& x4 |3 L, A2 n) N9 a, n* X (cond ((atom z)5 v# B( `3 d5 z" u1 r7 j
(cond ((eq z y) x)
) t5 W1 D# @- t1 l, y ('t z)))
* z1 `7 x! K! N" U" m# x+ } ('t (cons (subst x y (car z))$ I1 g: Y: t* G$ t& ~) E4 x" A
(subst x y (cdr z))))))1 \$ l6 e2 Y. j0 m/ w$ q
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
+ j3 j$ j# f6 A: z, ? h& Y4 x8 s% k1 d) z J+ B7 r& q8 O7 F0 u% h
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。
) X' V( ~& l+ _2 v( m7 F
3 ]5 ~/ D5 ? G: B6 F理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |