Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:
4 P0 N. l8 i9 H( s
' [9 T& ~" w+ N9 f既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
4 a/ I# o" B+ z在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
6 Z6 k, `5 m9 I$ D4 \# y/ s! q(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)) r" Z0 Q& [8 [% S' E2 c
5 U6 }) b. x7 d* w0 [1 y5 u+ l这一集中,我们将描述几个常用的函数,并给出它们的简单实现" u8 t# }- _* E7 F0 Y4 ?! `& Q7 _
5 O# `9 u) @8 |4 x, _2 `首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
$ l' n0 A6 x1 \" {. ?
3 p/ i t) P( D* f可能有些读者已经想到了,取第二个元素可以采用如下形式:
% r8 f# M4 K& T% c, \ ^
# `# q/ v# W; D+ t# {(car (cdr x))
1 L7 k. I! ~' J& ~ D3 H3 r
9 k* C: G1 M6 C9 @5 D6 g3 D同理,取第三个元素是这样的:' q' }: O* `8 m2 D _& E9 e
{. }* b- D* ?- B$ y1 A7 f
(car (cdr (cdr x)))
, d6 P: v7 n0 s C: V
8 ~+ u6 H( F: s2 `# |5 M事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
& j0 {; f4 ]' u) _
6 o. s" T6 x5 z) T' _% B> (cadr '((a b) (c d) e))! J0 Q1 ?. t$ T
(c d)
5 t# |; J; D* T$ y8 ?1 l0 z0 l( O> (caddr '((a b) (c d) e))
5 T1 ^' ]4 u# S+ Z$ b8 y3 m% ]e; d3 i, y6 ~: o/ f. \* c: ]
> (cdar '((a b) (c d) e))
8 q1 n9 k' W, N& E2 d9 @" Z(b)
s7 P% B% r. H* \7 Y* q) m
* r3 q& V* H7 s: a g3 m另外,使用(list e1 e2 ... en)来表示0 c4 o7 b9 b* b" v: _% j
(cons e1 (cons e2 (... (cons en '())...)))$ P: ~, k( n5 s! f9 y
2 u2 Z. H2 g3 c0 M8 |> (cons 'a (cons 'b (cons 'c '())))
4 i: }' v8 v k(a b c); t4 `+ a3 h0 ?3 T. k5 W
> (list 'a 'b 'c)9 b6 G0 ?, |1 h% S" L7 h0 b8 a0 a
(a b c)7 I1 r% T5 ]( i2 t+ D& }" B
% M" B' v9 I3 L# @
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
6 C4 R, E6 m( P' }9 |/ Z& S
* Y! b8 Q2 c/ n/ E+ I(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)& s4 d% c% D5 E1 {
Z" _, A5 s, N$ \(null x),测试x是否为空表。例如:
4 T1 }/ W$ i" {9 S9 i6 O> (null 'a), W. O, e0 l. t
()9 K6 o4 E. y( D# n: v/ X6 v, A
> (null '())6 w# u* T+ F$ E6 n$ k8 ?, U9 b
t
9 c0 `1 z. x B" Y6 B' a" j(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。$ q: s- {5 K8 B0 E
> (and 'a 'b)6 I$ a& @3 u" o+ T
t+ ~" @. `9 @6 ]5 i
> (and (atom 'a) (eq 'b 'c))* G6 m. i2 N/ A( l$ o
() + S9 M9 x+ e3 B2 E
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:9 J5 N( B1 i0 B7 p w) Z, l7 f: p
> (not 'a)
. b1 @: |( r9 Q0 [$ A()
1 B: h: H2 b. E! I> (not (eq 'a 'b))' o' f* |: h- E1 z/ k
t
, Q0 v' a4 C* m6 X, M8 u3 \: ](append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
9 x( v' {: O# o! O7 r> (append '(a b) '(c d))
& T* x$ J$ R9 }; H3 X* \(a b c d)
/ H1 [! ^7 [1 P: Z! F> (append '() '(x y))
. `1 n; p8 g# u! m. D2 w& C(x y) 5 U( L9 I! Z$ G% W8 i
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:
& `9 O- l2 u7 P8 {8 [" V# S! Z> (pair '(a b c) '(x y z))4 e0 H# @! O0 L7 h
((a x) (b y) (c z))
{4 @! S3 S& L, x5 y9 l(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:8 W) Q! c8 L7 @
> (assoc 'a '((a x) (b y)))
' J* Q1 I1 O. cx5 Q$ g- m" x) P- O+ L! h. f# @
> (assoc 'a '((a (foo bar)) (b y) (c z)))
: \! q+ G& I* f2 u" t0 e/ c# S(foo bar) 2 P0 r6 a) d" e
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:7 F9 @: g6 p0 Z4 B+ n( I
> (subst '(x y) 'b '(a b (a b c) d))
5 H, Q. O! J# d# t(a (x y) (a (x y) c) d)
& W' e% z; H9 Z: V5 _# D下面我们给出这些常用函数的简单实现:9 H. Z2 J) f! C3 \
+ M; D0 U8 P' V. A' s R
(defun null (x)
9 b$ h& Q4 Y; S% l! d (eq x '()))
8 i5 Y) J. F4 C3 v+ E(defun and (x y)
: K1 j: M$ d2 Z1 ?: ^ (cond (x (cond (y 't) ('t '())))
( m; W" p" E1 r- ~* J ('t '())))
/ |& E8 o+ N* g, O7 i* O# O5 u(defun not (x)
: z3 C6 n7 W' t- y9 S6 A- T/ C5 ` (cond (x '())! E e0 r# c/ G- J6 m+ o- l \3 p
('t 't)))
' a1 h$ F" X& z! J0 d: ^# H(defun append (x y)
# T% R$ m. O( _; ^. [& j7 \ (cond ((null x) y)
2 ?9 I' n3 L8 D! j4 Z# {; L ('t (cons (car x) (append (cdr x) y)))))
# c; H/ y4 K- ?$ @. j4 C# ^% U(defun pair (x y)
0 }; y* M0 |8 H" R# H5 {* O7 i (cond ((and (null x) (null y)) '()); B1 n5 s$ M; w0 T6 x6 W
((and (not (atom x)) (not (atom y)))" q4 m: q2 R, ?0 n. ?$ s) ]
(cons (list (car x) (car y))/ M# \% W( m3 E0 k8 [
(pair (cdr) (cdr y)))))) 3 o8 U6 M$ w: g- ^7 ` f. K
(defun assoc (x y)& L+ C9 l6 O4 C
(cond ((eq (caar y) x) (cadar y))" h: n, B9 `5 X: D
('t (assoc x (cdr y))))) 7 Y5 O) k* O5 O3 f% K
(defun subst (x y z), b- a0 K7 j4 }" s
(cond ((atom z)
! l, |. G* b" H) f+ N (cond ((eq z y) x)
5 J% c. s6 x# P7 C; w' v# W ('t z)))7 ~7 j% T, }9 p9 R+ `. Q! ]
('t (cons (subst x y (car z))
7 t8 r" l/ G' k9 e( w: g (subst x y (cdr z))))))
. Z& O, O8 u" s0 s$ k) H, b- C如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。. k- X% t6 ~) F& ^4 H
3 i2 o U% z" d& p# o4 K这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。8 B1 } H: f1 ` j6 @3 X" D i( o9 w
+ M! n3 z: i D理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |