Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:: S! g8 T) ^' Y8 |' s9 d$ d$ x$ K
! r6 c8 ?- K {2 l既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
! \! O9 L( Q+ ?: e) [. d) t# Q在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
& [; P" G( ^ j: @(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)9 V0 j+ _; g: `9 V- z( b/ b# J4 I
/ T3 v1 |4 S& c5 t. E- g4 [这一集中,我们将描述几个常用的函数,并给出它们的简单实现
6 z W9 `0 [" h. Y6 B$ @. F
a9 O( e0 H% X7 B首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?! e8 D) l: j8 ]* t; f
! g& W- M0 {1 Y0 J可能有些读者已经想到了,取第二个元素可以采用如下形式:2 q4 |) q- V% n. ?: `) S! L
1 y1 ?" E) {6 A) I0 @1 Q# x(car (cdr x)). }# c5 V% w4 C7 G$ j
! Y( Y v4 _4 `- |同理,取第三个元素是这样的:; O& O# S; W% K& l- ~
# M5 N" W8 p; ~: b8 h/ e/ ]9 P(car (cdr (cdr x)))9 F S# V: D- ]
" R W4 a; l% O事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
0 \3 k3 _) H. y$ G" p/ s2 |
, v1 w! L( L0 f2 g, L9 x; R> (cadr '((a b) (c d) e))
* y) S0 l" B- F6 H3 g(c d)! A$ ?, t( m# F' c" {% y$ t
> (caddr '((a b) (c d) e)). \ W' H+ R" N3 Q7 R
e
& M4 M& l' ]' l+ B( o$ _) F1 v> (cdar '((a b) (c d) e))
% B- h4 G' Q* T6 z. n+ V, G& n(b)
~* p6 x- c; K7 i$ Z2 L* g2 e; m5 h: ?+ K. c+ P" M
另外,使用(list e1 e2 ... en)来表示# w( Y4 l* p+ @
(cons e1 (cons e2 (... (cons en '())...)))
( \) s1 i; Q7 ?* E
% n( B( Y$ [: E* d. ^> (cons 'a (cons 'b (cons 'c '())))
$ d# {# y* S9 e. A(a b c)
; h7 i3 q) L) b> (list 'a 'b 'c)8 j! F$ C! P/ g3 v1 p# Y
(a b c)
0 G4 _9 B# C* w+ q( X
( M' u- o8 ?/ x现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
. y& |- X# y8 m2 ]. w$ o I. W& S' j! E! C, w# U, I" q( i G' ?
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)
, ~3 \& e8 x. d
( k4 `& _9 L4 f% b+ @! N(null x),测试x是否为空表。例如:
6 s, ]% G- z D* `> (null 'a)
7 q- e* b" E, j, i(). f8 N3 Y. J9 S* L; }7 e- o* Q4 W
> (null '())6 k$ j4 x. y8 O2 R; \# A1 w6 N
t
7 a4 p5 _& e7 b! `9 u(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
) M4 P' t) `7 c3 c7 f/ X1 t1 r> (and 'a 'b)
/ L4 X) i( S, z! ]1 ~5 E2 Gt
G% _9 \6 I- W1 v5 d> (and (atom 'a) (eq 'b 'c))5 g5 O$ W7 j9 f# k+ Q7 [; o+ o9 M
()
" f7 r( u7 f7 Z$ j0 L(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:! v" O- r2 y/ Y: ~
> (not 'a)
( O% `' t5 q7 g. f% q) S(), `/ q. D% [8 Q* L( g1 t
> (not (eq 'a 'b))
$ c2 X p- V* F8 j7 jt
% k; U+ Q: M. b6 K# M7 w" ?& g' {(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
2 A/ o( v+ K N; C> (append '(a b) '(c d))# C k3 U; \( H0 `* W7 d/ y8 J
(a b c d)
& y. Y! a, d4 _- p6 X0 V8 k8 K6 \> (append '() '(x y))
$ T# m. c9 S3 D9 ~! U7 ]! T(x y) $ i3 T; x7 `# D
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:- r: C* Y% H' P. x( G
> (pair '(a b c) '(x y z))
$ w t4 S' u" l/ _$ z((a x) (b y) (c z)) 2 P5 c1 A7 j* R2 G$ J1 ]
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:& E3 B8 `: H4 f$ [
> (assoc 'a '((a x) (b y))), ?6 H# \' k4 L
x
, [: H+ X& Q+ l% e$ ^2 k/ O: g> (assoc 'a '((a (foo bar)) (b y) (c z)))
- p& [# S* s" D5 d0 d; c(foo bar)
+ T/ B' l+ @3 } V$ h% E( |! X(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:/ b0 C' N& t5 c% C( E
> (subst '(x y) 'b '(a b (a b c) d))$ R% F" g, V2 X9 J) T7 w
(a (x y) (a (x y) c) d); i, q! H7 ], c
下面我们给出这些常用函数的简单实现:
& H( R- A% W1 r) F0 \" }6 n
* T/ v& b/ c. K# M0 t& D K" V(defun null (x) @/ v, b/ W$ [8 I) W0 v
(eq x '()))
$ [& t3 Y1 r/ t3 v/ |6 O0 X$ p* z(defun and (x y)
: T p5 ?7 Y8 p5 d (cond (x (cond (y 't) ('t '())))
3 |6 c/ _! M5 |! W1 H& e: t t) C ('t '()))) - q$ b9 B, q( O
(defun not (x)1 u# `3 L) c. o! W0 l1 R* S
(cond (x '()): n; ~6 U+ S# {/ m7 R/ j, p
('t 't)))
6 j7 s }3 X5 b2 _; S# I2 x5 L' D5 s(defun append (x y)
" L5 Q* v) s" g. w- ^: j (cond ((null x) y)4 V2 x6 c" L* d5 t6 _ g0 a) J5 F
('t (cons (car x) (append (cdr x) y))))) 0 R; l! }2 D! `' k& A$ Z* o
(defun pair (x y)- r: x$ w/ D# w. R
(cond ((and (null x) (null y)) '())
+ [( M8 B& }1 w) N7 X8 D' X/ q ((and (not (atom x)) (not (atom y)))' m, N( `0 f- N* d2 r: T3 D1 l
(cons (list (car x) (car y))
8 d* u, Y% a2 r8 i& t (pair (cdr) (cdr y)))))) ! G, O- ]& P: _5 y0 K3 J
(defun assoc (x y)
4 F# E7 r7 \/ Q: X (cond ((eq (caar y) x) (cadar y))
! m- Y9 z3 u3 p G; S$ J1 v1 a7 w ('t (assoc x (cdr y))))) 5 ~+ E3 }4 m: {
(defun subst (x y z)5 W/ T: m. Z/ d8 y
(cond ((atom z)6 ]# @ A+ ^6 ~6 S
(cond ((eq z y) x)4 ~- r2 n- a2 G+ l
('t z)))
' z) ]# S1 Z4 T( F5 s. m* ~ ('t (cons (subst x y (car z))0 G& j/ z( i$ K! c) b( B3 p; c) _! ]
(subst x y (cdr z))))))6 `# A' X/ m4 i s- s
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
- o6 T; e- A$ s# c
0 t$ C/ G! ~. _+ ?( r- I& y这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。
4 d& D# L: U" a* @" l0 E5 h4 O
0 Z. u/ }. v4 i+ O4 U理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |