Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:' Y* r3 H! l5 M0 \% n8 Q% N
) x1 A5 x% c) ~( ^3 S
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? 1 E& R" l3 N- l! H6 n5 Y) c6 S
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
# t) Q7 A( j) T0 t; k! n(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
7 I2 n9 }# ^% B& S' Q! i: a
- t! z7 Z& h* g# M% e' J4 V, I这一集中,我们将描述几个常用的函数,并给出它们的简单实现
p% J+ d" i+ I9 C# t% l( e" @8 |9 Z
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
+ W+ k3 C. L4 }( g( u
5 `) O. ~ R" {, S; q6 b0 g8 x可能有些读者已经想到了,取第二个元素可以采用如下形式:8 |9 p$ k3 l' V& e- g
# _' r7 ^ C6 l4 W9 Z* ^0 _( b X9 K(car (cdr x))0 T, ~5 p! p c
9 l5 g# l( R' Y" q j& v同理,取第三个元素是这样的:
$ W A* v/ G- \; n4 b+ y! ?4 s( Q9 m6 G& T( x
(car (cdr (cdr x)))5 v$ y7 H8 x! {9 z/ H' o! a2 G
# ?5 O$ M D( z, w! X' L事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
8 ?5 z$ q* R- b; ?1 C; J1 Y# S' w
, F$ F/ P% P4 ?; s- E0 J- v> (cadr '((a b) (c d) e)) F" D/ v% j; g5 t
(c d)7 O9 d, P1 }: h; [5 D: Z
> (caddr '((a b) (c d) e))& N# ?: k$ p& p/ d4 [
e
* H& p: W1 P v" r> (cdar '((a b) (c d) e))8 G% W5 S, K5 P8 u; E
(b)- E/ P. U! J. S
. u% p8 z n E P& I" V, u" W
另外,使用(list e1 e2 ... en)来表示4 p( r; o, F1 {
(cons e1 (cons e2 (... (cons en '())...)))" K8 D" g1 J( G0 a M3 K( ~5 y
, y( E( N7 I8 N& ~
> (cons 'a (cons 'b (cons 'c '())))
" V+ {3 {" ]3 n! c: s(a b c)1 {; C: F, n5 b3 u. {/ j b& }
> (list 'a 'b 'c)
" S4 S9 B. C8 |/ |9 W. f' Z+ i(a b c)
% J' }8 p; z7 V! z, m) l( f+ @
1 M5 E. O1 p+ c3 _3 I8 P& _3 @现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。1 F/ x# ~* N1 u# W( T; z
% }" a' `1 B! U9 c" g# b" n
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)& d% l4 a/ Z% M! {
! D3 l: Z W3 k& d/ q6 z5 W& @(null x),测试x是否为空表。例如:
6 ?9 q4 H/ y% d> (null 'a)
* k* N7 j' [. o()
9 t: j2 W# ~5 b> (null '())) y/ _3 O# Z% I- h
t
7 _# X# ]8 g1 P(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。- ~) f" w7 T) j0 k; A4 t
> (and 'a 'b)
' ]% |; c/ N3 ]t" d% K0 f5 V' a' c0 j
> (and (atom 'a) (eq 'b 'c)). n) H8 f: ^% |9 J
()
9 X: ~9 c6 ~; v+ ]; c3 E(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:& f" A$ \8 E. w8 s
> (not 'a)
' H/ s* T8 S! M& i L()% B/ \) a: h; j z% f
> (not (eq 'a 'b))
$ F0 X, L6 l5 s0 m8 d9 X2 xt ( A! U4 K5 F8 I$ f1 e% m! u) Q
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:4 ^* r# A5 u# K( e. Y! L. }3 ~
> (append '(a b) '(c d))
3 ^5 v( G0 {2 d2 z$ u(a b c d)
: h' C. C0 y- ^% f8 V> (append '() '(x y))
* D2 }6 S2 M: P( _, _9 N; G(x y) " x. a, w Q5 m0 W5 V2 V& l
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:- M" w6 i( I) `9 q. G
> (pair '(a b c) '(x y z))
6 R+ {% Y% d% t9 ^- X9 I((a x) (b y) (c z))
. g" c4 E- {. e, Z% h" A/ u: t3 D(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:
4 A! B' A Q, l5 a1 I( ?9 i9 B> (assoc 'a '((a x) (b y)))
3 a, u4 C A6 {" H4 ox9 M) T1 s$ t/ T$ v/ I5 N6 Z$ v+ O. S
> (assoc 'a '((a (foo bar)) (b y) (c z)))
5 _* n8 h8 G% }0 G k(foo bar) / T) h, p$ X3 t0 q
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:$ H* S$ s+ q, w2 Q1 l% y
> (subst '(x y) 'b '(a b (a b c) d))
, B) @+ }7 h4 ~7 Y' S( t1 I( F(a (x y) (a (x y) c) d)
" p6 O: }! F& d4 i a& ?# I下面我们给出这些常用函数的简单实现:
5 C9 h4 P6 B3 I$ Q$ u
3 _2 Y6 M; _; ~% m0 Q(defun null (x)
: H6 N: u0 P6 t& {8 j (eq x '())) ) n2 o# _2 H$ V
(defun and (x y)! {, K# t( P8 b4 b
(cond (x (cond (y 't) ('t '())))5 M# X" v" x% ]6 r$ T' a8 |7 a. b# A
('t '()))) 0 x( X# i2 z Z+ {
(defun not (x)
D( W$ K- V! G) B0 _" [' d$ S, d (cond (x '()). s+ k, w$ `# W* g
('t 't))) 1 z4 m3 E$ A8 L% @9 ^1 g, ]; q
(defun append (x y)
% l6 `& W1 A$ P. D" e5 F (cond ((null x) y)
, U' Z C: Z6 z ('t (cons (car x) (append (cdr x) y)))))
3 t1 {, W5 f. F5 m# j1 q(defun pair (x y)$ ~+ `) D8 W; n& Q0 p, j
(cond ((and (null x) (null y)) '())
) s$ e& ]% S# e- ?- H1 t! |' A ((and (not (atom x)) (not (atom y)))) ?- e) K5 }6 d
(cons (list (car x) (car y))
& W; u7 R" n$ u9 h S3 M( Q2 R9 t: @ (pair (cdr) (cdr y)))))) : g$ P/ q( Q8 t6 O
(defun assoc (x y)- f5 H' T9 G2 ^2 Q0 r8 e
(cond ((eq (caar y) x) (cadar y))
, N& \0 T) K' ?% k# P4 K' }+ ` ('t (assoc x (cdr y))))) 9 a& Y5 T/ N' R# \ N, Z
(defun subst (x y z)
/ P5 N+ Q& L! r" ~5 v (cond ((atom z)
! y7 p1 x* U" Z- c K5 _ (cond ((eq z y) x)2 m8 o$ Q1 K. L5 v1 r8 s0 c
('t z)))6 x1 W8 V, Q& o$ b, u9 t
('t (cons (subst x y (car z))9 c& \. U9 b. f% r6 R
(subst x y (cdr z)))))). t' U1 |/ R& n$ C' ~
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。- _. u4 S0 A; {- w
, D! N* ?' Z& b
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。
3 U* Z. {0 [" |3 ~6 y& O
- h: [' c; }- P6 m2 g6 j- m* z$ u, f; w理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |