Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:
" i4 \" X+ r( R1 S
6 A; b; R& K' {2 l: F2 {7 A既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
) @& Y9 n k1 U5 q* S0 w) F" a: \在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?& [9 x9 `1 q3 c& N+ S3 G
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
/ \2 u Y2 F6 P* W4 m- q$ T t& z3 |7 \, \7 A
这一集中,我们将描述几个常用的函数,并给出它们的简单实现
# W; P$ K) N3 [6 @! [+ _# A8 E5 q8 c9 {
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
3 C1 b8 {1 b4 y
`! b0 A9 S" Y3 e( t可能有些读者已经想到了,取第二个元素可以采用如下形式:
1 Z% b6 V% A w4 G* l m
& t9 t; @( e0 Y, [(car (cdr x))+ Y* s; H3 ~7 A7 S0 V# n4 J
& L: e# [+ I6 q: @0 Q同理,取第三个元素是这样的:
8 S8 k( [' ?6 h3 ]* V) z1 w* L$ G8 T1 [
(car (cdr (cdr x)))
9 w# {& q. _9 w3 }9 Q" O
9 t; q; y* C7 W4 b9 R事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
& A7 y* H* q. q/ q2 D& G8 L
( q* M- }. F/ H F> (cadr '((a b) (c d) e))
) F, ]; a. E, T' U(c d)1 k3 ~/ u2 }7 q) B! H& I
> (caddr '((a b) (c d) e)) S7 P- b7 f8 q' [
e, C) k, ?7 H0 o: Y a3 L" o# b
> (cdar '((a b) (c d) e))0 |& ~8 c& p% j/ S! {0 l
(b)
( Z3 n+ @' x# q, }0 F6 e8 A
. }, R3 P) @( Z3 _$ g另外,使用(list e1 e2 ... en)来表示
+ ~9 d: N4 ` {% y z(cons e1 (cons e2 (... (cons en '())...)))3 x3 m, p& F' E' ^- l
- a$ _# {* l% R2 l* w
> (cons 'a (cons 'b (cons 'c '())))* g% w. e+ l- {$ e5 g. Z& i6 n
(a b c)
. | d( X& a; A! A, }2 R- z> (list 'a 'b 'c)
& \' X. D9 h, C8 I) m% q(a b c)4 k8 E* u: Y5 n0 V6 x9 k8 ]
6 M+ X* W0 A% h/ O现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。7 ?4 ]7 o: }7 C0 w$ ~ ]
, r2 z$ S: z. M: D% a* L; G(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)3 L W1 h+ f1 ~, ~* f
3 ?" m8 x) C( E, Z2 J
(null x),测试x是否为空表。例如:9 A' N7 e0 z) x/ o- g% ]! \
> (null 'a)
0 x% ]! V+ O: b9 c(). C% i6 D5 n6 l3 h1 N/ s* a& j
> (null '())* l( J2 Z5 {2 w$ p# H
t
) w& L5 I+ p- }. L3 N* r" k# X(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
4 t7 T& y4 f% N; p" a> (and 'a 'b)
; J" P+ ?$ Q% g) C& Zt
- M0 w r7 F5 f7 P$ V( ` r3 p> (and (atom 'a) (eq 'b 'c)). i! Y$ ?) t' ^4 s6 H
()
- x+ ]# q6 c S% U7 p: _# W# f(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:8 l# Y" m9 A$ z% U/ G8 ~
> (not 'a)
! m5 ?' J* j& Y. P w$ o2 y0 l()
- h8 h3 w: B( Z* w4 F' d> (not (eq 'a 'b))4 U: u) }) u A$ A+ P- \
t
9 I- _/ g8 I. N( Q4 L; w8 c; M(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:* r% A) t( L$ `4 K* G$ b7 K& Q$ B4 i
> (append '(a b) '(c d))+ w* v3 c) v- }8 ]7 r
(a b c d)
- |, u$ g3 {4 M/ v> (append '() '(x y))
# I# d x$ T* v' x(x y) 0 Y3 x F; v, {( f! P, Z
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:+ ^8 }0 v0 R# G/ I8 u. o W
> (pair '(a b c) '(x y z))- K9 H& o8 @5 R( [. i, }) e! Z% I
((a x) (b y) (c z))
2 ^5 z+ `& H9 f( A! R1 J5 B(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:
! l. a8 a9 z8 G6 X0 N9 M) X) |: y) ?> (assoc 'a '((a x) (b y)))
2 w( { [% X4 M4 M, a: p2 Ox
9 w) L# P/ }: ]4 i> (assoc 'a '((a (foo bar)) (b y) (c z)))
0 O4 W9 A5 W( B; P/ i1 z0 X; Z(foo bar) - f& |) u2 L( X4 Q4 W. Y) {
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:# x% Y' n% ~' O/ `5 {
> (subst '(x y) 'b '(a b (a b c) d))
4 Q! t. L7 C& `( |" p(a (x y) (a (x y) c) d)
8 ~/ O" C! [8 h( r4 \# @7 d$ h4 R- T下面我们给出这些常用函数的简单实现:9 ^8 r+ a a! N% l% A# f
+ H B! S$ l; d* ~8 ^(defun null (x)1 O- n+ n9 n) r
(eq x '()))
6 m( p' g7 j5 t(defun and (x y)2 L7 r% q6 k: |0 ^
(cond (x (cond (y 't) ('t '())))
. U: E4 y0 ^. q+ v/ g, L5 Z% n ('t '())))
, l3 n1 d6 n' u7 x(defun not (x)
3 ~7 s! r5 T( L# M U# {, r* R (cond (x '()) Y( i/ a' s% R, E
('t 't)))
# r4 j8 `' ^+ B) B# d! |7 |- ?(defun append (x y)
" V7 {8 U* ?/ ?; v$ { (cond ((null x) y): ]: c+ ?+ S1 R; P7 `( u& R
('t (cons (car x) (append (cdr x) y)))))
. ^4 R# i, ]2 T. L, n3 w0 r(defun pair (x y)' _* ?. z) U( z. [" e4 u, ?. v; z
(cond ((and (null x) (null y)) '())
* k0 g/ _7 B3 Z- B. r ((and (not (atom x)) (not (atom y))) f( _: ?! z) Q
(cons (list (car x) (car y))
1 {1 p$ X% A7 X6 O3 c (pair (cdr) (cdr y)))))) 6 `# l( }( ?$ Q4 t4 k# s; _
(defun assoc (x y)' L& Z' c2 S$ ~5 @7 v( y
(cond ((eq (caar y) x) (cadar y))4 ?) c! \0 [; o+ W
('t (assoc x (cdr y)))))
. c* p7 b+ C$ S. V; n, m7 `(defun subst (x y z) S8 v7 h$ `. r& \' r9 ?: k8 X; k
(cond ((atom z)9 i) r5 U& W+ g( W* e k3 T, I
(cond ((eq z y) x)$ j4 ^6 m3 J8 R w: k5 |
('t z))), }% \- A4 L* I/ {8 {1 @4 Y
('t (cons (subst x y (car z))% \, f8 h9 O' S
(subst x y (cdr z)))))), x& k' U# F. ~" U! g- v
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。; R5 d* @7 ]' |/ @
* m( K$ O' }! Q! g8 ^* ]
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。0 j! W" h8 C8 Y6 m3 A
. D! L Y' I) V
理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |