Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:0 b7 f) [' \& _, \. ]
6 D' x7 \' I/ Z8 k6 z既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? + A M( h6 X# n# M" G- H1 o
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?% u+ _2 u3 k8 D5 R
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
! d0 f! O3 Y1 p9 d7 V7 e" e; D' F) R9 c6 @
这一集中,我们将描述几个常用的函数,并给出它们的简单实现
8 a8 c: R8 w, _2 _$ T" `% `! r8 G* c5 Y* }! w4 J' }
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
6 b2 L' _" \: V' B; U% ?! W# p# h+ P9 d) M4 Y
可能有些读者已经想到了,取第二个元素可以采用如下形式:( P1 ~! f. q, x, x7 ]
- N5 Q+ b# }$ i& n1 l, \' }: ~(car (cdr x))
& g% c) R- \, u8 D6 z7 N
- c: g3 F$ J E; x" b, b/ w同理,取第三个元素是这样的:
9 a; ~. k. P/ Y& x; w2 w# `
" @0 ]3 R6 u& @5 `+ q) G(car (cdr (cdr x)))0 Z* t5 Y2 z" h2 \5 M
" p P, m! ~+ F- M事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:5 x8 e/ |4 A' J7 q2 V+ k! A4 U4 O
+ i/ ~" ~6 P8 u* G9 B& \$ g
> (cadr '((a b) (c d) e))
C7 L5 I6 F1 o, H- ^$ G(c d)
; z: q/ I+ ]3 J6 u$ U> (caddr '((a b) (c d) e))7 _+ X6 ~! X7 x8 A- k- F
e
9 m9 W, J0 O3 d( u7 C3 a$ |> (cdar '((a b) (c d) e))
0 e; z# f2 ?. i' U- F, ](b)
8 m6 s( V6 F8 J* B* W* }& r+ } Y5 s2 Z3 E& F" m8 J
另外,使用(list e1 e2 ... en)来表示
0 z& {5 M% s% W4 `6 |(cons e1 (cons e2 (... (cons en '())...)))
8 _7 B% e- t& }; o* B4 @: ~: Z% T7 ]
$ O$ }& C6 H- ]7 N0 c> (cons 'a (cons 'b (cons 'c '())))* N6 |. F7 q- D
(a b c)
% t. F0 V& `; E% J. D> (list 'a 'b 'c)$ B8 o7 ` }5 z$ a+ N
(a b c)0 W9 L; C$ L3 X0 _+ d7 N
8 Z+ z! P9 g4 y+ ?- g; q4 K
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
, E% w, K4 ]: g# H, [9 J Y P* a. L# f$ E. e. \4 }! z$ J& b
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)
3 C- C( k: n% ]# J4 x6 Q7 J* F
8 g+ d* z U- u9 q(null x),测试x是否为空表。例如:
% L+ M. s( v7 L2 s> (null 'a)7 T2 J- o8 t% U' h0 A" U
()* x3 X- B+ m* u: G* a7 o8 i
> (null '())$ ~ W( K8 X+ ]5 q) _5 ]
t ! f2 j y: g; t' O
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。" [" R& Z5 }2 `; V7 g
> (and 'a 'b)
, j. o$ O; N1 \t
: @0 N( E9 X3 y) P! x/ L> (and (atom 'a) (eq 'b 'c))( V, T! d+ l- ~8 B1 ?
()
8 r* H0 W* R" z$ |- `, v(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:
$ r* h Y4 r3 ?8 U/ [> (not 'a)
$ K1 A' N9 \( s3 l; u ?()
3 A9 f1 I: }6 B/ V> (not (eq 'a 'b))
& Q! K8 ?: k: R g" f3 st 1 f! a5 L5 k" T/ l
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:3 u6 P) @% U) _2 [* d
> (append '(a b) '(c d))
0 S H8 a: E9 U/ J) `4 z" C(a b c d); q& |3 W! d. T* p* W
> (append '() '(x y))
# a9 ~0 ^' T2 J0 Q( ] ^. h(x y) 8 S$ j6 @0 k b$ y4 d- x7 J
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:7 A4 q: D5 v8 `' {" r. v# }4 v
> (pair '(a b c) '(x y z))
# E$ B( S$ @% w5 X. N) ]((a x) (b y) (c z))
$ V }* q) s4 n5 ^6 p; s! R(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:
" W: i! Y% x1 B! I( Z& A6 R> (assoc 'a '((a x) (b y)))
5 F6 A. O- {% V$ A7 Z/ @* Ux6 G4 s ?+ A) _4 i1 G9 S8 K4 Q: I
> (assoc 'a '((a (foo bar)) (b y) (c z)))8 X, Z9 W: D7 s9 h% K' |" ]3 ]
(foo bar) 1 M7 L1 Y0 A$ y
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:
# ]2 C% m& V Z; U7 |> (subst '(x y) 'b '(a b (a b c) d))
+ E8 V) z3 W6 o2 N- r(a (x y) (a (x y) c) d). q+ { Y9 ~3 J& q
下面我们给出这些常用函数的简单实现:; {1 }- ~0 e% _# p
" w7 n- D& l* H* l/ u5 @
(defun null (x)8 f3 {1 e z3 K; f. n
(eq x '())) & C/ W- V" s J; a
(defun and (x y)
2 u2 {; r2 X, p) P (cond (x (cond (y 't) ('t '())))
8 a A9 H& }- c1 D! N ('t '()))) - m C3 \, J* G& V0 L
(defun not (x)
. l5 M0 k! s& ~! t4 _) j5 s (cond (x '())
& @- s( {6 [1 q, E) T( \" E ('t 't))) ! w d7 g8 X# e$ ~: y2 r \# L' E
(defun append (x y)0 v2 C' Y) @, i' K5 p
(cond ((null x) y)
# `! S8 T8 d* f) x$ p& f) ^ j" I ('t (cons (car x) (append (cdr x) y)))))
% m" P U6 s6 d6 ?# ? F* t2 Y(defun pair (x y)
+ F! K x! R5 Y/ X( M3 u (cond ((and (null x) (null y)) '())+ R6 m% q# v1 P% @# H
((and (not (atom x)) (not (atom y)))
. d9 b" [1 f) \7 u" A) y5 b (cons (list (car x) (car y))$ f2 |! p( _( q8 p" y' ]
(pair (cdr) (cdr y)))))) 0 m" x, N& v' O @5 O' @
(defun assoc (x y)
+ u. P. V- f+ q. {7 C (cond ((eq (caar y) x) (cadar y))) Y: Z7 G/ ~1 c0 {
('t (assoc x (cdr y))))) : ?( u- U; v+ g" o! q
(defun subst (x y z)
8 J) |6 U& m$ C3 w# J* b: ` (cond ((atom z)4 h, W4 F# B! z& l) i3 U
(cond ((eq z y) x)0 V8 }4 Y R' P+ a
('t z)))4 c7 k! r, m) e w
('t (cons (subst x y (car z))
( @+ M. u- h5 A# K1 y9 { (subst x y (cdr z)))))); i* Y! U# J. i$ ~* q& |
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。* |/ U! e" q/ q) }' f3 |
, Y+ i. m2 N% D+ T5 j \+ H
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。
( B& l/ h# P4 d/ j( @& n# ~ T9 P% c: F2 U3 w5 J
理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |