Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:% K2 E! d" V9 q7 _3 A! Y# t* U
$ J7 e, B; n2 @8 B0 h/ C" B# |+ H5 O
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? ( C+ }; O" H/ t4 I* J
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
% d. `7 Y" f0 X$ ~0 |+ D) `6 d: d(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)* ]0 k7 A# p0 z1 X7 k( |
% h8 o+ C- p& J# d) C
这一集中,我们将描述几个常用的函数,并给出它们的简单实现
- @ z/ Q* u+ ]" Z L
; y! D1 y7 a8 ^8 c首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
& |' c e! y) c5 c
3 P I: k3 @) D( O, Q/ d/ U可能有些读者已经想到了,取第二个元素可以采用如下形式:
6 ^! B. l3 F4 V" ]5 y% ^5 @% z1 v( k8 O9 D
(car (cdr x))/ k6 m/ B: o+ q) R% K
) j. b6 x% g( d) j9 \& Q
同理,取第三个元素是这样的:; s J& t. Q X" T+ w
1 i9 a" N. H' G; }- w1 T0 y" u+ f
(car (cdr (cdr x)))" X: m5 d5 @. ~7 |. N
, v, `: @; }6 b7 D' w& [( b事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:; r$ o5 Z) y: m8 U; X
2 g& M! g& ~8 v* w4 J
> (cadr '((a b) (c d) e)); u- r$ ]0 Y# i% k0 z- I! @
(c d)
' v$ [+ z$ V9 v4 E> (caddr '((a b) (c d) e))
& e5 ^9 e! A4 v y4 he) g' {5 [1 E; K$ B
> (cdar '((a b) (c d) e))
: A" I7 e; v2 Y# F: G2 S, l: S(b)
, e3 z! W a: B
9 }( w; u- H; ] y另外,使用(list e1 e2 ... en)来表示' `0 X B1 v9 G0 [# b q
(cons e1 (cons e2 (... (cons en '())...)))
. x' F" E* a4 d y. \% T6 k0 e1 L2 k' u7 h
> (cons 'a (cons 'b (cons 'c '())))
/ O$ x5 [: v! G(a b c)" `6 T8 u0 V6 ^6 m$ s& D
> (list 'a 'b 'c)
: W+ |/ b2 x; G+ K9 [, ~# _(a b c): m+ t# J J# D* h+ J5 X
( W; C% k0 j! n4 L2 E- O% r
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
4 d2 ~) L- t/ `# W0 O: {5 [, g/ g; J8 X- `
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)# }9 p4 s% K/ J3 t/ m
, D( k: X% k! g* [7 q6 s
(null x),测试x是否为空表。例如:2 w1 {2 B' e7 A& F; e. p0 q
> (null 'a)1 A' d7 R9 f9 K3 }; V
()
* L. r2 n" ^ l/ D> (null '())4 |& u0 g) Q! F# ]( G5 k2 k' p3 `& [
t
* C; X5 h' K- r9 s, S(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
) V, @2 |# N" |' L> (and 'a 'b)
; K- a0 O' J1 j% G, At
. g0 u1 `6 ?$ \" Q/ p% x! u, Z> (and (atom 'a) (eq 'b 'c))
6 R! B" j3 d+ H2 ]. l ?& D* z0 n() 2 T7 {; Z8 c. ?( G$ N
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:( u, T% \; c0 T7 t ^$ P7 V
> (not 'a)
6 f* k! i4 r1 `+ @()
% k2 w+ H7 F! t7 } A> (not (eq 'a 'b))# w: F5 O9 @2 M: a% g
t
% ]8 |( }- t, ]' k# m( t, Y(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
4 y. q$ g" @3 Y" @# ?> (append '(a b) '(c d))
) ~4 C. X' V! A3 v+ }1 D& T% v(a b c d); r' g( q. _: I3 m
> (append '() '(x y))
: h1 R! u. J" k; ?(x y)
& {0 B" Y- A3 e(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:$ T% c0 T9 A5 P- t
> (pair '(a b c) '(x y z))/ v* Q0 \# s$ L- X+ Y6 D
((a x) (b y) (c z)) ! w1 p l2 l# D& W
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:& o. I! J" m/ u5 l" ^+ i6 O
> (assoc 'a '((a x) (b y)))
% @+ ~$ ~6 T& }$ V+ g) ex0 A8 s' N% d2 ~& Z
> (assoc 'a '((a (foo bar)) (b y) (c z)))
9 M# b5 R! j% \7 q8 H(foo bar)
: U9 I: a7 c$ g* O! q2 M' q$ w(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:
# p6 m# d/ J8 k! A3 u8 z- r5 u> (subst '(x y) 'b '(a b (a b c) d))
* V: q0 s% e5 y$ j: ?0 m(a (x y) (a (x y) c) d)* c% e1 Y8 A& W& C
下面我们给出这些常用函数的简单实现:
& w! g! d; D- |# |1 Z. c% J0 e/ H8 i9 F8 ~8 l# a
(defun null (x)
( b3 Z9 V; x( a, P (eq x '()))
* R' q' b4 X# P(defun and (x y)
6 l& m- E: O, G& h; t, y7 G, C (cond (x (cond (y 't) ('t '())))8 u5 F" w4 b6 b( I3 D8 _
('t '())))
4 P$ k- H4 T. b' C# Q(defun not (x)8 ^$ m% w$ G- c) s" q
(cond (x '())7 k: e, G% ?- p/ h! X; {: d
('t 't)))
2 t G, y& ]$ ?1 n' v1 V0 ^/ ?(defun append (x y)
) q+ E3 r" N& B% M, |$ k (cond ((null x) y)
2 e4 K, ]' m0 N P" A ('t (cons (car x) (append (cdr x) y))))) ' E `" m2 c4 z3 `& w
(defun pair (x y)
2 P1 j9 h9 }( s6 G8 b- j (cond ((and (null x) (null y)) '())
4 f& G4 e; Y0 F( ^9 a) w# f ((and (not (atom x)) (not (atom y)))
4 y- I4 k1 {7 n/ S (cons (list (car x) (car y))% i( q( V' O$ {' c0 h
(pair (cdr) (cdr y)))))) % ]( ~& X: @2 N4 S6 x i" U
(defun assoc (x y)4 _# l J* C5 b7 O5 T) x
(cond ((eq (caar y) x) (cadar y)), t) v; w/ z5 {- u$ P
('t (assoc x (cdr y)))))
) U* \2 G3 i/ N; R(defun subst (x y z)! ]: O; C- _/ S; ]. u! g; B
(cond ((atom z)2 O& F" S$ t/ K
(cond ((eq z y) x)
, l6 c7 B- C8 U1 G$ s! n ('t z)))) d" t5 Q/ B! x" t9 u( W: a
('t (cons (subst x y (car z)). V7 O. N) ~. @
(subst x y (cdr z))))))
: c9 {( |9 q) O" u* r( R如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
: W+ Q0 Q6 r- n1 I- {
+ O! }+ x) G9 m8 r6 z$ ?这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。$ K4 L( V. L9 D _9 P* S
" }. G: y, u0 ~: G理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |