Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:/ V! X. c9 c, r3 r; a) ?3 ?8 Z
$ T$ u+ q/ C* v6 ?! Z5 [
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
# y" r( {. n- a1 S, \! C在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?8 @- }5 v4 H! P+ ~# a- Q G
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)% D+ |3 r- m$ {/ w& v* L' l
6 Z! K0 M, k8 E% I9 ~
这一集中,我们将描述几个常用的函数,并给出它们的简单实现
2 P$ v( q: w* u
% O3 c2 r. |2 O& U( o4 T- j首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?5 b0 c4 K9 T( a( [5 h$ l- U
5 K/ d5 B* L H( [4 Z R6 `" q
可能有些读者已经想到了,取第二个元素可以采用如下形式:
: u- [# w, A+ C% n& Z
# B% n' k5 [. \: `(car (cdr x))! c4 w- F. O2 v6 c) P
8 W0 I0 u' W) p' N- F* T
同理,取第三个元素是这样的:( u( }0 E2 Y, Z- M- x1 z
8 M2 I+ J$ N7 u/ W& E2 b9 B0 ?
(car (cdr (cdr x))): o( R3 J. d" _- ]! B
: u( d4 d& ]2 K {) A( Q, h& t事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如: i4 G G( Q9 j, h, y5 Z* G
- l" ?7 b! ~/ k+ P3 V8 @; N4 B
> (cadr '((a b) (c d) e))0 q1 L; i2 `3 v& Y" K5 ]& K
(c d)8 C6 a8 ^8 V5 u+ k$ g
> (caddr '((a b) (c d) e))
5 W' q) W4 n+ A N- {e) w4 e, e* z3 G8 b$ Z* U& C
> (cdar '((a b) (c d) e))9 ]. w8 E, \+ J; }0 X: f0 p
(b)2 A0 v. |& q2 Y
5 I0 |& ^' w+ w8 N
另外,使用(list e1 e2 ... en)来表示
1 u! m0 R5 u0 E& {$ V( m9 P(cons e1 (cons e2 (... (cons en '())...))): h* b: W% W% i+ s( ^* {
' d5 J; l) M' i" }+ R m
> (cons 'a (cons 'b (cons 'c '())))
9 p( Y4 }. G/ B; i. m6 H(a b c)
. J; m4 E2 ?. l3 w& g/ Y> (list 'a 'b 'c), _5 u7 w. X8 T/ }4 y2 E5 _
(a b c)
) I4 b7 Y& z1 j5 T
( {7 Z9 n3 G' W( p8 Z* g现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
9 \% ` l8 p1 O) W5 ?5 ~ k: m, F. h1 W9 K
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)
8 p3 J2 t( ?2 j6 h. M! j9 d8 w8 O
9 N$ C+ u; I+ m) e8 w4 u(null x),测试x是否为空表。例如:# T- N) J: }! B% Z7 c9 I
> (null 'a)$ a' R% ?" L9 ]
()
$ d% |' w" T" n( c- N> (null '())+ \5 x# S" G0 ?8 o3 q4 G! F
t % ]- s% O. G/ r" c# I
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
; x2 H) B' X# k* o& x6 m> (and 'a 'b): ^( F& Y7 d; U. J7 [! x
t
1 ^$ J* `+ w* _; q" Q# R5 C> (and (atom 'a) (eq 'b 'c))
8 r# F) C& g, _3 G6 H) B8 {" d()
0 r# A/ g+ \% ?(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:6 w* t) f0 Q0 {" {& \! u
> (not 'a)- j& A& N4 [$ Q
(), p o1 }+ A, H; ~9 N' R# y( B
> (not (eq 'a 'b))
3 O. r# |7 t& q, h; _t # w, Z1 I. e& O0 k* q; n' h9 X+ ?2 \
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
) d, U% J* x U& i* b( Z. M> (append '(a b) '(c d))% q2 N: \) }4 f: o g9 |, G& J, `/ B- s
(a b c d)
% l5 _* [9 _. b9 |2 e> (append '() '(x y)). c& I( S) o9 L
(x y) ; R7 M1 B4 x$ ] [" o
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:, }# O( @) }/ j& i! Z* t
> (pair '(a b c) '(x y z))
/ Z3 w3 N# |. A! g((a x) (b y) (c z))
, z# q/ x& S H0 e* M(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:) ?" p- `' p( O
> (assoc 'a '((a x) (b y)))
0 X) L8 l' q3 z- R8 G5 @0 T7 V1 Zx& _( I b- Y. Z Q1 b- q( @$ z) k
> (assoc 'a '((a (foo bar)) (b y) (c z)))/ {9 P; F2 _/ x$ Q3 {! O, i
(foo bar) 8 {& B& g- k* S/ E2 d( Q3 ]
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:1 N1 d9 h, p+ j+ v% I" [
> (subst '(x y) 'b '(a b (a b c) d))4 y+ r+ A* `" B0 o/ v
(a (x y) (a (x y) c) d)9 B. [! `3 p' X
下面我们给出这些常用函数的简单实现:
; E) F# T" q; R3 C( k$ R3 _2 p5 K4 K# }- ~ U3 q8 H# V
(defun null (x)0 Y0 i9 i) `* m0 `$ L9 V7 J" s# s
(eq x '()))
, N3 p" ]2 D, I0 r# y) K(defun and (x y)
* }, @3 p# I- h8 \* _+ J0 g (cond (x (cond (y 't) ('t '())))8 X- i: l) T5 ~' y/ f* W: C/ p
('t '())))
1 A& v& b6 o! i% I3 z7 N(defun not (x)
$ v5 i7 R1 G$ ~ (cond (x '())
. o# a& c+ L" U* x: o3 h ('t 't)))
U$ Z" @3 \: z4 `4 z(defun append (x y)9 ^) k( ]5 E+ `7 {) Z8 S
(cond ((null x) y)
2 ~, H% b4 ?1 D" ]: N ('t (cons (car x) (append (cdr x) y)))))
9 k# [, b6 x: W3 `% c/ p(defun pair (x y). d# ~' }2 T- u: q7 [* V9 G
(cond ((and (null x) (null y)) '())
+ b# C% }% i5 n+ I7 t) w0 U' ?0 z/ H, F ((and (not (atom x)) (not (atom y)))
( J, |& i' [" {" ?. p: C2 D (cons (list (car x) (car y))
2 X0 s8 {2 b4 j5 P. R: v (pair (cdr) (cdr y))))))
6 d9 \+ I! V# b$ D(defun assoc (x y)
! o5 |" o/ \! e+ A5 V (cond ((eq (caar y) x) (cadar y))
% N+ \- t4 k* E% Z! A5 \7 ~ ('t (assoc x (cdr y))))) * {2 ?# X* R" Y( m
(defun subst (x y z)
& s+ c+ O9 N, y% Z5 M: Z (cond ((atom z)
0 L' E4 ]+ d) @0 @& N/ P (cond ((eq z y) x)4 t( V0 \ Q; K
('t z)))
1 I' Q/ I9 ^& ~. F ('t (cons (subst x y (car z))% |' |% o) y0 [% U+ i/ c# G
(subst x y (cdr z))))))
9 b! k7 o$ J5 t* X4 d7 ?如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
2 S- e9 Y% Z0 J$ s; {
- r) x* D, A3 D0 u _& C这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。5 i: [5 A. @% _' A9 g1 ?9 U' m4 m
, _- B- ?( f# F5 j理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |