Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:
7 @0 h- x, Z$ P2 Q. Q
0 S, J8 D. g) l$ \0 z既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? / _' U/ k# m( C5 C" W
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
9 D3 D0 V6 z0 [& V(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)# c$ ~* O! c. ~' ?
( K9 g3 ]6 O! Z, z- F0 d( [6 f
这一集中,我们将描述几个常用的函数,并给出它们的简单实现
" D1 L1 n: {* P. x5 t; W" R/ g3 F& ]1 o. W/ `& R& z
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
2 L; y2 I4 R# Q1 ]1 s) m5 m( m$ [$ \' a* l
可能有些读者已经想到了,取第二个元素可以采用如下形式:0 {" V: U$ |- @/ q' N1 X$ @
[7 [ Q; G; C& v! `5 }* z(car (cdr x))4 v4 K8 e# A9 u
. j4 c, a W6 F6 |( f
同理,取第三个元素是这样的:
; _: @1 r) X m6 Q& e
+ _3 u9 l M; p(car (cdr (cdr x)))" \+ |2 U. G z* \2 G, L" w
: E: y* [7 z0 T( O事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
2 o8 i& ^5 o1 f$ f
& W% b4 r" g+ {( X4 Y! _( C> (cadr '((a b) (c d) e))! Y5 s' j5 T+ M) V3 j9 H) u/ I
(c d)6 I8 P) M# k' x# h+ \& u. I
> (caddr '((a b) (c d) e))
- S" C! k7 r9 W9 f: S2 @e2 w; v+ ~' g5 r$ X8 u
> (cdar '((a b) (c d) e))% f8 u1 M4 |, S* }: n" G; _. G! P- @
(b)2 g- q- Z- H% k
3 U8 Q$ x% f( ^( h3 K* k- H+ j. B. y
另外,使用(list e1 e2 ... en)来表示% b7 E3 D3 e2 V3 h
(cons e1 (cons e2 (... (cons en '())...)))
l1 ]5 T# e4 @) q7 a1 E/ l F7 v8 h, ~+ Z# }) F9 r
> (cons 'a (cons 'b (cons 'c '())))% K; I' S4 v c b4 @$ L4 i9 X7 `
(a b c)
& a( @! G. u# B! l> (list 'a 'b 'c)3 Y; X, s+ ~8 t j) ^* q/ `
(a b c)
+ n+ m. j% H* e6 o- T/ w$ e7 o) n
0 c' [; s* r7 l+ ?& ]2 ]现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
1 }# A' a- v# K; Z4 i, q) x, Y' c" g2 k8 Y: z
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)
/ `1 X) v8 _" w7 [6 _3 f0 n/ g$ S( d2 a7 T& B: p8 n9 j
(null x),测试x是否为空表。例如:
0 @. x9 A' j# \# s8 R> (null 'a)
z( r& J/ w6 u; x% w()
& a- i2 r3 D4 T4 j/ W$ |. j> (null '())
1 K" M; h/ T/ tt
/ M* t1 @ |! n8 e) m(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
M. h# d4 i' H# M6 v) M4 E> (and 'a 'b)) _$ W; x6 v1 _! ?; N% B5 S& i
t
+ H: m3 f4 e$ O* z6 D! \5 J> (and (atom 'a) (eq 'b 'c))
7 g' e) g7 M; Z/ }3 V& A() " Z2 p: K9 o* V
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:
9 g; J" H0 t/ C' S% ^7 B8 \7 S> (not 'a)2 V! l6 v f# S) V" ]5 f, M5 M
()8 \# C8 z' J* ~: y* q) o m
> (not (eq 'a 'b))1 Z+ N# H: T1 [: M9 K6 k+ Z( ^( H
t
! O2 n0 C) ]6 ~# u(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
7 N/ a0 i& g# I$ X: }( O% _* ^7 z> (append '(a b) '(c d))
# C T/ V# q6 P5 {(a b c d)+ \5 h: x/ _% `/ y& _) w: h* s6 X/ K
> (append '() '(x y))' N' a& ~) O6 Q2 i4 h
(x y)
6 g+ x; ~1 z7 P+ ?1 y% ?(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:
, }: I. C7 R" v/ C7 ^% b; Q> (pair '(a b c) '(x y z))* S7 K& A1 L$ A; \; a" f
((a x) (b y) (c z))
" B. ?8 X; W7 q& e% m# C(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:2 f$ I- D, b( q- r8 I3 y! t
> (assoc 'a '((a x) (b y)))# @* u; F! S, o7 j) x
x o1 j. B& Y* [4 h: H8 e- L1 n
> (assoc 'a '((a (foo bar)) (b y) (c z)))9 c6 c7 j T' Q6 F- d
(foo bar)
2 e3 V0 i" {; ?2 Q. Z- N(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:' m: b' T0 Y# H" o C% `
> (subst '(x y) 'b '(a b (a b c) d))
0 K, Q7 I" {" ^3 F* W. n- h(a (x y) (a (x y) c) d)1 m; m+ t W j' z) L
下面我们给出这些常用函数的简单实现:
# f) B+ {8 R: d7 p) y3 ?' X/ _7 n, v& Z/ R
(defun null (x). E' ^- y1 T( h% p4 G/ m; i
(eq x '()))
/ e3 k5 ]" r2 h) [(defun and (x y)( h4 q" W" x7 [+ y6 x# \5 V+ k
(cond (x (cond (y 't) ('t '())))
4 ^9 N2 a, [" ^+ S* f ('t '()))) & `4 L7 X1 P$ q& I
(defun not (x) S; t8 s- D" r% Z3 X
(cond (x '())
. ~. o; ~9 a4 O L' }7 _8 O ('t 't)))
v0 Z+ D: o7 s- X/ L4 i! z(defun append (x y)+ ~( i! c% ]6 h+ i
(cond ((null x) y)
6 X+ A4 I; [, s ('t (cons (car x) (append (cdr x) y))))) * e: s/ r0 s- ?
(defun pair (x y)
A+ y$ B/ \$ O+ ^7 Z+ v (cond ((and (null x) (null y)) '())$ r) x$ v |! B7 o
((and (not (atom x)) (not (atom y))); G" S2 J' H3 D
(cons (list (car x) (car y))6 u" y8 E9 W$ K. K5 Z
(pair (cdr) (cdr y)))))) 3 ]* ?- [4 e, c4 u
(defun assoc (x y). ?% l- N- j5 |, e7 o" A7 `) _) D
(cond ((eq (caar y) x) (cadar y))
! |: Y; A! |! g4 M- f ('t (assoc x (cdr y)))))
" ?' U* L$ B) s; j9 h(defun subst (x y z)+ @) g+ a* S6 @: y: L; O
(cond ((atom z)
: Q t7 q0 z$ y* x- ~ (cond ((eq z y) x)" v% k7 C3 T/ v
('t z)))
; z% g F2 y& X! C ('t (cons (subst x y (car z))' r) I2 B5 m6 D0 p9 s/ n
(subst x y (cdr z)))))) ~# b) x9 Q3 i8 W) g
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
) f8 S6 U1 @6 E# N6 V% B$ l8 C" ]/ a+ d o0 k, F Y+ f! d2 A
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。
7 F, D* C, f' p2 Z6 C& L6 A$ M7 g) _! i8 c9 U( S6 |, y
理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |