Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:5 U7 g: A8 Q' u2 `( m4 @5 N
_0 l# }7 i0 H0 ]既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? 9 L, r# W) r0 \
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?* K$ G" U1 O2 H* V! ~: L" R4 \& |0 C
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)+ D6 t s1 [; ]+ a! Z3 f- r
- d) v% J4 L- r4 Q# s/ t, h; t
这一集中,我们将描述几个常用的函数,并给出它们的简单实现1 w7 g9 g$ k' o4 D3 \6 L( t
: m. J% q3 ~0 _% C2 H首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
5 _# ^1 c, w5 {" k& e( |% V" W+ G8 T! u7 O, T; b& y. t; x
可能有些读者已经想到了,取第二个元素可以采用如下形式:
) ~6 i& h6 i2 N) H1 p% T, a/ Z" M9 \, a
, ~% y. f' s( m! T* k(car (cdr x))" e' Z" i( E5 ~
; X4 W# ~1 B. f, e( n8 w2 K同理,取第三个元素是这样的:7 h8 L7 Q. k, j( p/ q
# {8 G3 I5 K: N0 l4 `(car (cdr (cdr x)))
a8 ]6 y( j; b. M5 C4 e: \) J+ o
% u1 L% M" b) V( Q0 y事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:! [3 e( a4 J: B! @2 c
( j, k" r9 @* u! D3 n3 V8 {> (cadr '((a b) (c d) e))7 t* R( `* V* R
(c d)
- M2 C0 P& x& b1 h> (caddr '((a b) (c d) e))$ D3 X$ i. O/ |7 F
e" [% W! o8 i# S# `- t& Z! _, r6 u
> (cdar '((a b) (c d) e))/ |$ V% \; z7 \4 j( e) m
(b), _5 U- g% C7 I1 O, x' l: |
5 o3 G% k# W+ s- ` _4 c
另外,使用(list e1 e2 ... en)来表示
2 a- j; v. {8 j2 h(cons e1 (cons e2 (... (cons en '())...)))
d' z6 D6 a- o9 \8 M8 N5 c7 |8 Y1 R* Z+ G7 K' K& e& ?
> (cons 'a (cons 'b (cons 'c '())))' s! `$ G" ~- A: Y z6 Z2 q4 s D
(a b c): n5 N2 H9 l3 W& p; j4 M4 G! S
> (list 'a 'b 'c)
% V7 Z2 l0 u" s: p(a b c)- U' b8 n5 I( s5 L; @, ^
: v1 k$ w4 H% U1 L# I% `现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。
3 l- y S/ E! h( \* m# Q8 ~- ^; i2 e" D. { E$ H
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)5 m; ]1 k; R% q' u3 `$ p- L! [& W! G
% i4 _) j" `% n5 ]$ R
(null x),测试x是否为空表。例如:
& r& d' @: x8 U. A( h# @5 b! R4 R9 N> (null 'a)* Y0 A5 K8 Y. A8 U9 }
()6 P9 t+ ]4 `; I! i- y( H
> (null '()): G" ^$ u7 k+ v7 }7 R
t
4 v! {1 m; i" d4 T$ p1 R9 e(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
5 W5 ]1 A1 k/ N+ q2 r> (and 'a 'b)' J5 H: T6 U2 z; K
t1 B0 ?. f! ]1 ?) c
> (and (atom 'a) (eq 'b 'c))/ P+ e4 U6 A1 S
()
" K* _( j# z, F% k6 S$ A(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:
, U/ h5 ^! ^2 B3 {> (not 'a)
) K6 |. P1 L5 d n( \()$ }& p( Q: [5 P _$ ~
> (not (eq 'a 'b))
2 f& @0 ~4 b4 Ut
/ u- U" y: F! C/ F, v2 F% D+ J(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
( q! v& i; l: o> (append '(a b) '(c d))8 D; N" P8 L) a) G, G
(a b c d)( C4 ~. D# ]! }* k- I, X7 J. E
> (append '() '(x y))
& c* I9 d* F! q6 `(x y)
" Y! w/ c; k% b3 x! E+ y; k- W(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:
* f K: n4 h$ H0 j3 r> (pair '(a b c) '(x y z))
9 E a' C, h) S3 s/ Y5 y((a x) (b y) (c z)) + x, n: F* W. T# ?
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:. g, E2 S- }' z2 s; V
> (assoc 'a '((a x) (b y)))
+ {: Y( U6 P1 k$ b# ~* yx
9 a: _, a+ g" `> (assoc 'a '((a (foo bar)) (b y) (c z)))
9 `- Q& P7 `0 l7 V9 u(foo bar)
3 P* N& J, [2 q, J R(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:
/ h$ ?# l \' X) N' L4 E( M7 A( k4 X- |> (subst '(x y) 'b '(a b (a b c) d))9 x J" M: g: j1 t
(a (x y) (a (x y) c) d)* c# W- e! P& D. w2 {5 }. H- l: J
下面我们给出这些常用函数的简单实现:
% y' G( i5 F2 p) C. ^" K1 v( X8 r4 T' ^0 u- R8 A) s6 x( B
(defun null (x)
( l' p) R( K) L7 e2 q2 `! [$ S (eq x '()))
9 X. y9 _* P5 u# F3 y1 J(defun and (x y)
$ I- t; v2 g0 O' G; [ (cond (x (cond (y 't) ('t '())))8 Y7 a) `: Y$ g" f9 J3 t! G6 X
('t '()))) ! Q5 v o+ O# Y& }6 p1 W4 \3 ~6 F
(defun not (x)$ K3 g" B6 X9 N( E( r) W
(cond (x '())
5 b4 _, ?! |- R% }4 ?( c8 C! m! A ('t 't))) + N2 P$ T3 R# [& e6 b& Y
(defun append (x y)
/ [ K+ q3 m" \ (cond ((null x) y)
7 S4 L7 i" {# u3 r ('t (cons (car x) (append (cdr x) y)))))
& N+ W( z1 E4 Z9 k(defun pair (x y)
6 l3 r' k0 D% D; I (cond ((and (null x) (null y)) '())+ M3 C7 F% w' K: I( p: h
((and (not (atom x)) (not (atom y)))
5 {5 q& F0 N* u3 v% O1 f (cons (list (car x) (car y))) I# ]1 q$ ^/ T. U3 A3 Y
(pair (cdr) (cdr y)))))) 6 \2 ^/ N4 G ?5 D4 l5 j' a5 X
(defun assoc (x y)( b8 I) O* C6 v5 E' z, c
(cond ((eq (caar y) x) (cadar y))/ }6 C7 C( W4 \3 d' N
('t (assoc x (cdr y))))) ; k+ `# y' u3 |0 A6 t; k
(defun subst (x y z)
: d+ A a- x- r (cond ((atom z)
8 V2 f$ P/ U# f' e. G (cond ((eq z y) x)
! F3 A% J' K% _4 _& b ('t z)))
4 c1 |2 [' g4 t8 d ('t (cons (subst x y (car z))
4 r2 j1 c$ D: B; ]+ i (subst x y (cdr z))))))
3 I' @% j/ o1 Y1 |3 F如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
0 o4 w% G/ P v5 y/ _3 b' U7 ^4 |
/ A& `) v! \3 m$ G: Q' h这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。5 G' f6 j4 Z9 _8 T
1 F) d2 ]/ Q- [+ ^
理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |