Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:! C7 w7 e! [2 E9 e( e1 y
# u( j8 u' [% H: J
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? & Q# W8 ~/ [! O9 ?5 t
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数? |0 ?# o. }" f
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)2 ?. T/ c( g" B% ~7 a2 t: ~
. j$ e( P, N) X' |$ i2 f
这一集中,我们将描述几个常用的函数,并给出它们的简单实现9 O+ c( Y, w, v. O8 r* g$ [+ @
T" n% B7 `7 y& p/ c
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
" K1 g( d# J5 U1 T2 U* X
$ O; V! d# Z! H+ i% D' z4 I; E0 x可能有些读者已经想到了,取第二个元素可以采用如下形式:7 Z6 v0 n2 Z$ w
% S% Y% o6 `; s6 `: J* S
(car (cdr x))
/ b; Q8 r7 {5 m _3 R/ J. ], L" D1 | F) o; R4 v
同理,取第三个元素是这样的:
( b7 Q$ `' m$ V0 Q5 h
6 v. x o3 [. w( e% d/ ^(car (cdr (cdr x)))7 [( Y) C2 D# ~; |" d* T0 t' U6 {
$ X2 h" A9 u! h5 ?; a2 U1 q
事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:8 n( {& E+ c+ L& V, J
( K5 [$ ]- y5 U3 W9 G0 I
> (cadr '((a b) (c d) e))8 Z6 _; Y, w) M9 L
(c d); A' Z( w, B* m
> (caddr '((a b) (c d) e))' U* X3 V- {% J4 w i& n
e
) z0 Z) P- S( f1 u1 x$ o> (cdar '((a b) (c d) e)): q+ i# |% c8 c' }) I# Y. v3 \; G
(b)6 v7 s$ b; G6 v! F( }2 y
3 w* k ], }: a0 O7 E% a
另外,使用(list e1 e2 ... en)来表示
; c8 e8 A/ _6 ^9 L9 I4 N5 O(cons e1 (cons e2 (... (cons en '())...)))
# @% o1 h+ l! u- c3 p+ x) I5 j( C* |3 S) S' v5 C
> (cons 'a (cons 'b (cons 'c '())))
( v$ {1 ~+ A' D& G6 `9 R(a b c)
7 j+ L& }3 Q& U) y& j> (list 'a 'b 'c)$ ^* h+ l; D ]" C& c1 L( a$ M" u
(a b c)9 [ Q- ]1 x' A8 j( \' m
, Q- m9 I: C3 S% M" y
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
2 ?7 G8 Y+ q$ r1 R; G7 A$ u5 H2 X6 o% d7 x) O
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)6 H' _! C. [8 y3 h) |
! F0 D9 U8 D# e ^
(null x),测试x是否为空表。例如:
0 C5 I6 B- p; Z1 [> (null 'a)
, [5 x9 t8 M8 A+ A()3 Q5 B/ ~ y- e$ G9 H! [
> (null '())
- Q6 ?3 [; _" G4 h: Qt
6 h+ o, [* P9 G8 [& i/ b(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
. `5 H6 o+ D6 ?! k> (and 'a 'b)
' e0 l& b9 y/ T) E- C- g! m4 T* ft
5 g5 ?$ v# `6 T0 i+ C) f# P, R> (and (atom 'a) (eq 'b 'c))6 R, o. [. { B, o" V5 D
()
2 A" q( X w' D, W. R% g(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:! o+ Y: a" \0 |) d# }
> (not 'a)
3 o! H, ^" z% P) d' y; z/ v$ a()
. p/ V8 o# z6 b; Z: z> (not (eq 'a 'b))
. f0 b* U+ t8 v4 b# w) q9 `! bt
/ s2 h! P7 X; d+ v(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
) w* a8 a0 @7 ^' P4 g> (append '(a b) '(c d))
- e# ]' j) q' \. C(a b c d)
5 e$ }7 K3 ?/ W. {> (append '() '(x y))8 d% O2 L) M, V( y
(x y) 5 k. A5 |# G9 c x2 N
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:. {) h8 M* r- T. y/ W) {% Y" F
> (pair '(a b c) '(x y z))
% b$ m% S, N8 i8 x2 }% [5 s# t$ D((a x) (b y) (c z))
/ H1 f9 w& _. ]) D. w(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:; }, x+ O( J5 P% |1 f& t
> (assoc 'a '((a x) (b y)))2 u; H4 }' T; z. R( q* ?& g% M3 r
x# {5 U: d6 _, g. f6 @3 M
> (assoc 'a '((a (foo bar)) (b y) (c z)))
1 J1 V8 ^' q. L, i& N% Y' Y6 u% X(foo bar) & p! z1 u1 B) m1 F* S0 ]8 d
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:0 J, |! n3 c) ?* ^0 b% b
> (subst '(x y) 'b '(a b (a b c) d))- g3 [) V! P; l% U
(a (x y) (a (x y) c) d)
7 V/ t5 r1 N" }下面我们给出这些常用函数的简单实现:3 o7 V; d/ C% K1 w( m1 P' w$ \2 A
( C, j5 P$ a/ s4 o" v3 |9 [) j. B
(defun null (x)* l( N/ W0 l" V4 Q( w4 W9 _2 f
(eq x '()))
: g1 I4 W- F: D, T! }3 X3 B(defun and (x y)/ g' t# m) Z! p4 v7 F
(cond (x (cond (y 't) ('t '())))
! k; W- m7 y; T# f$ o5 H } ('t '())))
9 j: |2 s g6 [, ^( M(defun not (x)7 O. @, z: J0 Q
(cond (x '()) D8 B V& S: ^& p
('t 't)))
G, ^0 l8 q/ f' _(defun append (x y)
. y3 U5 E5 C$ @, I) l& Q (cond ((null x) y)! J# b' r3 Y7 e; z4 l
('t (cons (car x) (append (cdr x) y))))) ; S4 E- I7 s- J8 P# B5 E2 C/ c
(defun pair (x y)
6 J9 Q! X4 y; l8 o& A (cond ((and (null x) (null y)) '())
5 x. L. ^+ t: f* v- x ((and (not (atom x)) (not (atom y)))9 o1 J2 J2 P5 q( {$ ~" y- M! A
(cons (list (car x) (car y))0 S* L0 R+ J% o, e5 E" I6 o- B* ~
(pair (cdr) (cdr y))))))
7 j" }& |5 ~- H- x3 I8 @: w(defun assoc (x y)+ V& M; i7 K+ e6 V8 \
(cond ((eq (caar y) x) (cadar y))
3 b0 V5 ?5 |8 h9 j8 t ('t (assoc x (cdr y)))))
, F3 a2 b5 _" J% p% Q(defun subst (x y z)$ C: J9 x8 Q* d4 y/ `
(cond ((atom z)
# B0 {: ^' b7 w% x4 t: G | (cond ((eq z y) x)
; _7 C+ r5 C8 R ('t z)))1 B% V3 S" d5 O4 ~4 h( F" n
('t (cons (subst x y (car z))- n" u& }% b d9 \1 C
(subst x y (cdr z))))))+ y( l# @# T% i7 K) ~4 }# v0 E
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。' \7 r6 s: S, ^: c
; w9 d' @& r2 f% \" R4 ~
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。& S: z0 p% \% i/ }7 K. Z/ j
9 O, s9 T: T) Y, v0 c$ o, |4 r理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |