Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:. d7 ?; s2 J& |3 e! b H
6 h- z% i: X. J u+ D$ `0 m9 b
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? + K7 D3 K7 _+ L* i0 b- y% a5 t
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?2 W* n9 O6 i5 _4 r( }( ~: z
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
+ [$ {8 u1 F7 v: N- C: S% ^4 ?* j$ i) y
这一集中,我们将描述几个常用的函数,并给出它们的简单实现
& D, h; |* u4 l8 s! g/ s. _) g, j8 x7 N7 Z9 L! s' A' h9 l7 B
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?6 r* D$ [/ y5 Z4 o8 Q3 M J4 t8 }
5 ^9 D+ z2 P9 U5 m, c可能有些读者已经想到了,取第二个元素可以采用如下形式:% M) I, m2 K8 Y1 N* L2 v
- g( ?: \% g1 \6 H* {(car (cdr x))$ c: g7 Z6 }) ?& R; t3 u- n! P/ u
w1 j5 I. _6 z( J7 Y同理,取第三个元素是这样的: F3 @3 o) D1 [; d/ E1 @9 h$ \
+ K& N7 [0 \' s% {! Z(car (cdr (cdr x)))/ k7 H0 {$ X0 |0 P6 w
5 I( B$ E! N' a
事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:; A' {6 t5 u- r( f2 `5 R6 T
+ N+ V" K3 P* B# n, o> (cadr '((a b) (c d) e)), X5 q0 h, Y4 U
(c d)
9 z/ b: Y* N, {: |6 k$ L> (caddr '((a b) (c d) e))
3 t* r* ~; O+ O% {! g: he
) `1 J% O {) H+ _4 M2 t# i% z/ I: A> (cdar '((a b) (c d) e))0 n) Q# L: q3 U7 t# R
(b)
) K$ ~6 D; c1 ~- l5 J3 {
, O- g4 z4 A4 _% ]/ U另外,使用(list e1 e2 ... en)来表示
5 ]* W' p0 Q( `$ B* s0 |1 S* y) l(cons e1 (cons e2 (... (cons en '())...)))$ l, }3 M0 v1 Y3 n7 A$ ~3 c
5 X& o) e3 s0 C> (cons 'a (cons 'b (cons 'c '()))), K9 C/ Q6 d8 O, T0 R" ?6 C) B9 z
(a b c): x0 u6 m6 U1 q8 Y& T
> (list 'a 'b 'c)
% Q: M) l1 O1 l6 W. `(a b c)
1 N! R0 h, R3 F* M, A% O! d( W% |2 g/ M+ R: l/ r8 X% F. e
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
& m; d) P! s; M/ W2 i5 }7 b* B& t! |; t' b
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)
7 E, h1 n- g& v! t. ]
4 S; X% e$ |) N( H(null x),测试x是否为空表。例如:2 K6 J* c0 b O1 ^6 U v
> (null 'a) _8 A8 N; [7 `# q
()
' |& d2 k) c, s' F( \! E& L& Z> (null '())4 _ b/ ~: e! y6 Z
t
' z0 x. ?, \2 [5 \# x, }1 O(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
. T0 l; U/ |) X, o> (and 'a 'b)8 O8 y, w- F$ t7 K3 N
t0 }. F$ c6 B% p
> (and (atom 'a) (eq 'b 'c))
0 g: b1 j( a3 a()
) y0 H8 _& O- E" w+ Y(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:4 G/ Q* b+ f, S+ e
> (not 'a)
& b/ V% E5 ~8 y/ o5 j4 i() S# k/ U) r2 u% b- S
> (not (eq 'a 'b))2 s1 A7 P! F% O
t
& Z9 s* e3 D+ m+ Q# I+ p9 q, i- F(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:7 O9 B1 E. Q. W# f" k% q& B
> (append '(a b) '(c d))* y8 p; n% D# _1 |& G- a$ U5 z
(a b c d)3 D$ g0 `% u0 \
> (append '() '(x y))+ x7 H. s- w$ s& @. @" S
(x y)
% E: L5 D2 y" h) C% M8 t(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:
+ I0 n* Z' e: s$ a7 p* y* U> (pair '(a b c) '(x y z))' O' c! Q( Q& r- |4 `
((a x) (b y) (c z)) * ` M/ z7 L, q1 { F" V2 U1 j
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:
K) o% h2 O+ D> (assoc 'a '((a x) (b y)))# J3 J! U( [ ^+ m
x5 c3 {4 g! ?( L9 k/ N
> (assoc 'a '((a (foo bar)) (b y) (c z)))3 P5 t( _' [7 u/ C- A( q
(foo bar) 6 ]- c, F" F1 H! Z% I, c
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:+ j/ J# j7 U0 B0 g: b6 i4 h1 E
> (subst '(x y) 'b '(a b (a b c) d))0 R) C5 u! \, b3 {( D/ |- B
(a (x y) (a (x y) c) d)' F: C" m% B7 i, g5 k
下面我们给出这些常用函数的简单实现:( k3 c# Q) k% q w
. j9 D0 T {8 g! b+ a2 F- j(defun null (x)
% v2 k$ b& L$ m' B O (eq x '())) & n# ]! { W* y0 |4 r
(defun and (x y)
) t9 {/ F" y" s! p5 I: L (cond (x (cond (y 't) ('t '())))
/ ^7 n1 o$ b, |* p+ z$ ~ ('t '())))
$ v9 H& P* |/ b; v0 L' y# ](defun not (x)9 \- \# [/ J. x2 ?
(cond (x '())
8 f& V! ]7 v! e+ [0 _ ('t 't)))
9 z) P* U& p5 l8 J) k(defun append (x y)& _9 Y. P% n7 r! ?
(cond ((null x) y)4 B/ T6 N2 \: b* B; g9 x, b
('t (cons (car x) (append (cdr x) y)))))
' g. ]! P5 q/ E5 |$ f2 D& L% |(defun pair (x y)2 l4 ?$ |+ D1 W" V; d' o2 p2 S
(cond ((and (null x) (null y)) '())! V$ ?! x" ^( Y. G4 L! \: s
((and (not (atom x)) (not (atom y)))
& Z8 I9 o& @4 ^. d$ k, }; o (cons (list (car x) (car y))
, p5 T5 B, K6 h+ c) v# ` (pair (cdr) (cdr y))))))
- ^8 A/ ~' q; Z, J- g* I. r(defun assoc (x y)9 D B+ N+ p) h; l
(cond ((eq (caar y) x) (cadar y))5 z/ w+ ~5 v9 U& A$ @3 M
('t (assoc x (cdr y)))))
: v) j2 P6 P+ z% x! }# r) |. _(defun subst (x y z)
. t* [/ G \5 s( v) u- X* A (cond ((atom z)8 q; B i3 p: s4 B
(cond ((eq z y) x)# b5 J: }% f H b' |
('t z)))
9 S! ~& J0 R% A' w2 ?0 P1 n ('t (cons (subst x y (car z))
, ~8 d6 R* f# y" o$ O' Q6 D0 y (subst x y (cdr z))))))
X7 p$ a9 r! f4 p) B: N如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
; v3 [. z7 V- J5 o' E
9 q) d" ]# T# B5 I+ Q这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。
! W" r& k K" J* N
6 [3 B2 N/ U; ~+ Q( t% o理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |