Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:
! X/ m9 _' f3 w" m# l/ @& M( U$ \- a) w7 b. F- {1 Z3 M% m
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
2 w. H7 H9 W, V$ i4 Z1 L, h在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
* g. @5 ~& b* S; C, k2 N% l(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)1 e! X C; Z# }
% v5 A V/ B/ k2 Y6 r
这一集中,我们将描述几个常用的函数,并给出它们的简单实现3 f+ {! ?8 v, m% f H# K
, y2 ]/ d! |* V9 h9 t Y/ n# J
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
& h. t3 K3 M! Q+ P1 h9 {1 D
2 s* z6 p" B- ~4 f8 F可能有些读者已经想到了,取第二个元素可以采用如下形式:- q- M: B; v9 l0 H" N) x! ^
# O* E' I& u& c/ V j, E# P" n(car (cdr x))
- X; z+ ?8 }( g% U- s( R7 r2 \$ I2 Q9 b" k* ], U5 d8 J, |6 g
同理,取第三个元素是这样的:6 N+ E' R$ f( Y- e
& Y) p" r! E2 v
(car (cdr (cdr x)))# n1 N+ N% n3 w* ~$ ~1 k) s- E0 j
v O, N, [0 w' y7 D事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
" F- @+ d% i/ o( x, X
5 X9 h2 Z# D/ m# x) w) O2 o> (cadr '((a b) (c d) e))& `4 z% Q) p8 I9 p
(c d)8 v, G% ]5 o( g3 `: X, T
> (caddr '((a b) (c d) e))' q" C5 C$ A" l; c% s% T2 k# w9 m7 I
e
5 K% h y6 u' z5 ~: e7 C+ E> (cdar '((a b) (c d) e))
7 ?4 r0 x# [$ j7 r* S# e: w(b)
3 D4 h Q% T8 g5 u9 z3 m+ F# e" j: m. `8 d) |. H2 u
另外,使用(list e1 e2 ... en)来表示
# r: n3 s/ N/ ?(cons e1 (cons e2 (... (cons en '())...)))# x: s( l- }6 o+ K
' \. P5 \' j ^0 K& h; G' s% }
> (cons 'a (cons 'b (cons 'c '())))
' s' N8 a" u5 d6 g* J: Z(a b c)
- ^, i. J$ h% F- [! G, V0 v7 b- Q> (list 'a 'b 'c)
* ]( u7 a( w2 N! s) A' M0 e6 J(a b c)3 d. v1 ?8 I& T8 l% _
1 K i2 {% X. x/ ]
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。* K& }; B! M2 t. t& P; I
; m- l0 \& H2 [: n2 I(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)0 r5 ?) P) i" ^, ?
( g% I/ w( `! R7 E9 ~1 {' D1 t3 K4 {
(null x),测试x是否为空表。例如:1 @& Q" \& d, C
> (null 'a)5 c+ h+ J* K" a
()
4 }/ i8 C" ~8 Q( M& h+ H' X: Z> (null '())
l% Q0 T1 i3 y+ l5 n P& \t $ p$ w$ ^) z+ V+ M& y6 o
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
8 @. f$ M# k2 _, K8 u> (and 'a 'b)( M* [6 j: x! k2 {* }7 @% m
t
& c5 a5 @( \- _; m0 C0 D: ~> (and (atom 'a) (eq 'b 'c))5 s# p! {' i# I) j6 D9 B) W1 }
()
( c3 O: g8 n2 m" p6 U(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:. f$ p. B+ N" s& i
> (not 'a)
0 I% R' I+ o# l3 O1 @()
$ n0 | a1 k! [( Y- q: D> (not (eq 'a 'b))
2 u9 L0 I* E3 _' \5 w$ It
2 u$ R6 l+ S* i. {- ~- F(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:. @0 L ^2 l: c% D2 c3 ^
> (append '(a b) '(c d))
1 |: ?7 L" L/ H" L) w+ b(a b c d)
$ i3 f, ?* I* t0 _* u> (append '() '(x y))
& [" Z! g5 B* B4 `(x y)
" i: z0 T& R' r) K8 w) S/ {7 \(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:
3 R4 a. M5 L9 @1 l> (pair '(a b c) '(x y z))
) z& h+ [: r, `4 y3 R((a x) (b y) (c z)) / F' n8 f, I4 \' s; _3 @
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:
0 G# R- e' U I; x> (assoc 'a '((a x) (b y)))) y! `. j% m( P$ Y4 i
x
0 |9 H/ w/ d! q" ?$ x8 B> (assoc 'a '((a (foo bar)) (b y) (c z)))! }" l- H2 V8 x+ @) e- x, W. a
(foo bar)
& {$ [# D, ?8 o" \) Q/ L! u(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:/ j! o4 t. I- j4 U# c8 Z- Y
> (subst '(x y) 'b '(a b (a b c) d))
1 ^5 H5 Z, i! {. z9 N: s(a (x y) (a (x y) c) d); S; o2 L5 w$ m! K6 \$ S6 M
下面我们给出这些常用函数的简单实现: T" I* d4 v6 L' z; A/ i8 M
& ~9 G7 {. e" L/ t% S+ J- b
(defun null (x)& v0 P+ |6 ^, @. _8 }/ V# F
(eq x '())) d9 h8 L5 ~# H7 E% r" Y
(defun and (x y)7 k: |; n( M( v! |4 N* f: N
(cond (x (cond (y 't) ('t '())))
# m- P2 g5 t6 l5 h+ g ('t '()))) . E1 d# h+ [4 c% B3 O, c, @4 s
(defun not (x)
" L) l; {, P9 i; G* C8 ]3 k2 y (cond (x '())+ C8 v! V% a3 T) r$ j
('t 't)))
0 W- V9 [8 }- ~; W, e(defun append (x y) B& T( a$ t# u) B+ }( w. |+ D
(cond ((null x) y)
8 x' z6 q) N! _3 v% m2 ?% F8 r. q ('t (cons (car x) (append (cdr x) y))))) ; m/ I3 l+ V# `/ H4 v+ J/ V
(defun pair (x y)
$ Q E/ m0 E8 y2 s$ M7 }: W: o' f5 c: W4 b (cond ((and (null x) (null y)) '()); g& X- D Z3 a2 U% i
((and (not (atom x)) (not (atom y)))' Q! U! D5 |( P
(cons (list (car x) (car y))
& d6 j3 t: A3 I (pair (cdr) (cdr y))))))
1 ?! j7 {# i# V8 b+ U(defun assoc (x y)
! w' J& z: m! w2 I3 r (cond ((eq (caar y) x) (cadar y))
9 }/ A$ d2 v% C8 |0 m) E ('t (assoc x (cdr y))))) + D6 {* Q* Q; e k r* n
(defun subst (x y z)
' A# b! i8 n% k, j (cond ((atom z)0 v" N D" @( c8 y9 K5 I
(cond ((eq z y) x) H) p {0 o- i! P- `
('t z)))/ l" s2 i" l) _
('t (cons (subst x y (car z))! r9 b0 \8 u E) F
(subst x y (cdr z))))))
@5 d/ k! b, L/ e% i" d" t" T. c如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。$ N5 Q( `5 H: g0 I* g
2 N! \9 I8 ~" N. b% W3 l& J这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。% R$ z9 ^' H& i. E0 S" a9 v$ Z
$ M$ ~" }. Y+ [0 ?+ {) Z- N理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |