Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:
6 r3 m' ?8 C( d5 c* K3 t& v; _* u
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
. |. e6 x! k% P, u3 L) M3 c在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
9 u" U! W) J3 z9 [% H! p(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
3 A$ L+ R0 v7 a6 g4 K! i G7 B+ t, T" l- o* x7 I) [2 w
这一集中,我们将描述几个常用的函数,并给出它们的简单实现
- K2 |+ U. J. t2 @9 R. q3 j6 d4 e" w% v$ y' [1 f
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
' s, {- ~0 C5 C1 ]$ ]. L5 O5 P: p3 R3 }- r1 a& m/ L- o
可能有些读者已经想到了,取第二个元素可以采用如下形式:
: v1 ~9 O- B( f6 i- N6 V; @! |! U
, _( w( Y) ]: ^! l) B6 r2 B% A& m) w# h(car (cdr x))0 @& \ B* b! v2 p& T1 \" S
`4 }. w, [: p2 f* ]同理,取第三个元素是这样的:. K4 T1 E _3 i& E6 n" ?
1 a3 s' O" s7 m4 `* x
(car (cdr (cdr x))), v# o0 `5 M- Y, C& u
% Y* b* y: N. T# E( _6 m6 D
事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
. D. _" S( K8 [# [) p2 x% I$ J/ _* E2 O' S# s* y; \: \: M1 i" ]
> (cadr '((a b) (c d) e))
/ O m8 p! \7 ^1 Z5 S, m' r(c d)
3 i& I8 a2 ~ i> (caddr '((a b) (c d) e)). H) M$ o8 n% Q! Y) f& S' T4 N4 Y. e; e7 n
e
# U2 R/ h N, W2 V8 H> (cdar '((a b) (c d) e))
F$ F9 D- `& A0 [1 K: n(b)
) x0 w) X4 h) y4 R0 v- q
$ P: p l( a) A: \; X F另外,使用(list e1 e2 ... en)来表示6 f* V# o: B( S6 [. E
(cons e1 (cons e2 (... (cons en '())...)))" R( m$ p O; ~
7 H' m2 F" _; l t/ z6 y7 m& M> (cons 'a (cons 'b (cons 'c '())))
' A f* V7 a0 f9 [" W2 z9 ?, G5 {(a b c)) z4 z2 P, ]6 c" \+ E7 \
> (list 'a 'b 'c)
' J. x8 Y$ h7 A) y; Q" [7 X" z4 R(a b c)
: ~4 J* X& H1 G% E
' M( N b1 k/ q/ J3 e) W& x9 U现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
, W6 U, Y3 [1 u& a# p/ J" |* N1 n% W9 ]$ V( y0 c8 E
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)/ \ o! b+ m" D
; F9 S( |7 f; F(null x),测试x是否为空表。例如:! X! }% x' V9 T# R8 s Q; v
> (null 'a)
2 D0 G: P" ~0 u; F# ^5 J(), p0 `! H2 R& }& s4 ~
> (null '())+ L3 t: C1 B9 D1 |) t4 {4 |- {
t
, o1 m( C7 r, T(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。' O+ A3 G& A2 b; U/ _
> (and 'a 'b)
. V- }$ C, R) k' {4 F$ v) s, \t
+ Y9 ~6 G) e* ]& X> (and (atom 'a) (eq 'b 'c))
5 @1 `" N* H& W3 w1 I1 g) v; n: t() * ^% n* ?" q1 u' ?
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:
; }* |$ \$ o$ Y7 l8 x> (not 'a)
4 h4 N4 v! F( [()/ \# ]1 D" o8 c! S. q
> (not (eq 'a 'b)), Q0 s3 e% A/ p% E7 x4 q3 b! k$ C
t + h% J' ?6 W) Z( m3 {5 p
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:9 k( E+ g! V( `* b
> (append '(a b) '(c d))
7 {# W |" q4 O(a b c d): N% P1 e5 l2 Q: N6 t. U7 l* J* G" z
> (append '() '(x y))
1 e9 O0 i0 n6 F5 X/ [! ^(x y)
' q: d/ C% t: e! O7 f; p(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:1 V4 x4 B/ d: }/ Y
> (pair '(a b c) '(x y z))
9 ]9 q9 E1 s) w3 Z; P((a x) (b y) (c z))
: W, [* o1 Y8 |0 ~& a8 N+ i(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:
' e6 s: ?/ O: X* j& V4 @( ~; ~" a1 s> (assoc 'a '((a x) (b y)))
# s- R8 ~% d% ~7 T3 y% y0 A& C: Ex6 I" v4 ]/ c8 O5 s4 M0 k- r
> (assoc 'a '((a (foo bar)) (b y) (c z)))
4 b& L5 @: z; T0 e' S6 V; c- t(foo bar) # b( u" E9 Y# ?1 ]
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:- H$ d' G, G- Z% e6 C; |# |1 I
> (subst '(x y) 'b '(a b (a b c) d))( C% a4 i; @, \! e- s0 }; X
(a (x y) (a (x y) c) d)
. s5 x r; W# H$ Q2 B下面我们给出这些常用函数的简单实现:5 J& B7 q; {" S, L1 @) \
& [6 r/ F) b. ~6 @5 o" P7 o% r(defun null (x)
' i& k2 g0 ?* L! j1 T( X& \# ` (eq x '())) 3 b! L( [+ {1 H0 i* x$ ~ c
(defun and (x y)9 R" A0 v% g, ~$ w" k0 J
(cond (x (cond (y 't) ('t '())))
) _' M3 F* h, ~0 | ('t '()))) 0 W7 _/ U( n4 ]% Z! S9 K
(defun not (x)4 N+ k" {- I0 ~$ I+ _
(cond (x '())
/ c% j5 b4 X) V ('t 't)))
8 K, i/ J( v& _# u: M& S. A(defun append (x y)" e, W9 D. V2 n/ G
(cond ((null x) y)2 H) B# E+ G C9 ?2 a
('t (cons (car x) (append (cdr x) y)))))
N! v2 E0 K& F" e s9 ]9 I5 U$ x* f! M" i(defun pair (x y)* n7 W9 M( E: Q+ E8 i9 I! t
(cond ((and (null x) (null y)) '()), m- a8 ^; F2 s. j
((and (not (atom x)) (not (atom y)))
0 G/ n& M+ m4 w: u (cons (list (car x) (car y))
# C$ T2 w2 H) E; P4 m9 i (pair (cdr) (cdr y)))))) 0 T* J8 E, P% X8 S
(defun assoc (x y). L1 E/ P' ^. d7 }0 V0 l% u- V
(cond ((eq (caar y) x) (cadar y))) ~& C7 C0 S7 @7 n6 |) J5 S3 y/ B
('t (assoc x (cdr y))))) 9 W0 T% ~+ {# U4 M8 `
(defun subst (x y z)
1 C4 f) G p# K4 m" e% | (cond ((atom z): u3 ~: }* g% X. ?! w$ b
(cond ((eq z y) x); N) {: z9 f- _1 a/ H
('t z)))- r7 s% z/ t% k
('t (cons (subst x y (car z))5 o3 D6 M# `, T
(subst x y (cdr z))))))/ k8 p/ |9 V# z/ u7 I
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
T- ]& v+ t g7 G- {$ W, W' f8 `& P" q9 X% L `1 l2 X
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。& _( c! [4 `2 S2 B" r
6 S& I6 X e4 x
理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |