Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:2 A( A/ W/ F; \' x5 R
$ G, \/ y% s& W4 p' m6 [* I既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述?
! T4 O+ S8 c% @6 m- G N4 s1 `& \在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?) D5 p0 m% ]% P1 t+ s: B2 y3 l
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
7 t' c) {2 Y3 f4 c3 K9 A' y9 ]) x& H: {5 B/ u* }+ E3 f& ?) [. Y
这一集中,我们将描述几个常用的函数,并给出它们的简单实现
; N1 O/ n5 j6 ~% j, C& }& H8 m% I* v; Q C2 F
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?( t1 I) L6 j9 T7 ?7 a
$ K( ]; ?* D2 z: d: v
可能有些读者已经想到了,取第二个元素可以采用如下形式:* p* {3 t" c' P; a2 q6 W) [- r! ?
4 q- \1 d& j2 _$ D4 h3 K
(car (cdr x))+ |: o& l) a* M3 J
# t9 I* d, Q! {) t6 O
同理,取第三个元素是这样的:. ]" q5 {+ ~# U* e
5 E. |3 D$ u$ n2 L& B/ ?: I6 v(car (cdr (cdr x)))
; I( \1 g4 M) ] {3 |! x
$ w6 y" w% A/ z# {0 u& G事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:# V& T- Y& Z$ }' r% k
; t: n( `5 f$ n1 Y3 x5 d> (cadr '((a b) (c d) e))" s* ^4 L- |& q" v! K9 r: f
(c d): [ ^) T+ S4 S, A
> (caddr '((a b) (c d) e))1 A5 n/ A) K, A
e
8 ~2 l3 N+ S& J> (cdar '((a b) (c d) e))
8 n9 e* W# K+ m7 m8 o0 x(b)! q ?9 g! O, d; R. i
1 l6 X5 o! a0 v0 p, j3 F. d另外,使用(list e1 e2 ... en)来表示
" z' | k- v! E/ A(cons e1 (cons e2 (... (cons en '())...)))# f9 `+ h+ n( o6 t) o
4 ]) x( G4 Z& Y* N, a7 U> (cons 'a (cons 'b (cons 'c '())))4 \. E% w C& o
(a b c)
& a8 z( ?1 Q& x+ a: D5 T7 u> (list 'a 'b 'c)
1 Q6 @- Y+ u4 r) N8 k4 S! c(a b c)
) P, `5 e( j8 A4 P; l3 z! P R7 ?2 x2 d5 ^4 }' \ h# r* C6 ?
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。' p) d8 ? p, \; u- u
& z5 A8 i8 o5 \3 q( W% d3 ?9 I& k+ L
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)
& [9 b: U0 h0 f. @* T4 u" K0 e
# H L+ p; d0 N6 B) }$ d(null x),测试x是否为空表。例如:8 \4 K( P3 d8 y5 q& ?& B
> (null 'a)
& B3 }3 Z+ h7 V1 U# u, H()
: ~- a5 s% l1 E1 p> (null '())
: t: I7 s& D, | D, Y/ zt 3 I. z1 e( B+ t9 M
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
~+ L8 k' R1 T( V> (and 'a 'b)
% h# a5 o$ A) v1 jt$ g( E3 Q1 M f% `
> (and (atom 'a) (eq 'b 'c))
4 I: y" S6 I! ~% j5 Y() 3 {; i+ ~3 J/ l6 Y; }/ Y3 I" ]
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:; b& j- { f) @9 v$ z' l+ f
> (not 'a)
! Y# j) e8 `5 e- n- }()# b0 O0 f% Y! ` }& a. I4 C
> (not (eq 'a 'b))
+ A; x3 \, o' H! yt 4 f4 F: k3 \3 b6 T
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
! _, R5 f' O6 [; ~> (append '(a b) '(c d))
' M8 b2 k4 [9 Z' o7 U% d, J(a b c d)
7 e1 z7 c5 O7 E- z> (append '() '(x y))7 Z& c3 _, Z; j+ |, W, T! ?
(x y)
' F; @) C* F6 f. v(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:
, }3 G1 u# b0 w/ ]* L, `> (pair '(a b c) '(x y z))- b; q& l4 o' e
((a x) (b y) (c z))
* G( l1 a" z: M(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:" ~) e+ }6 U4 P/ P! r2 g, q7 t
> (assoc 'a '((a x) (b y)))
, C ^: s, |' D: O3 Kx
! X! O- d) u0 N7 h3 r2 Q& W* U> (assoc 'a '((a (foo bar)) (b y) (c z)))* a6 m3 e- l; J8 K1 Y
(foo bar) K/ v0 }" V5 {. E
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:, U1 b7 O0 X! Q, f! c2 O
> (subst '(x y) 'b '(a b (a b c) d))7 d8 |7 G6 ]: W) k" W0 D8 X
(a (x y) (a (x y) c) d) W4 m+ D1 H9 A+ e
下面我们给出这些常用函数的简单实现:
( P6 [- B% {# T
! w% Z5 U7 F5 Y( g! e(defun null (x)
1 b) {- W2 L4 W+ X1 k* j6 {, Y (eq x '()))
8 B, p4 F) p3 N! e3 n/ A; P9 K, e(defun and (x y)
. x+ f9 w( {: M) O9 H, L( a% x (cond (x (cond (y 't) ('t '())))& I0 f& u! H1 q7 [. F) o& i
('t '()))) m% Z; y1 i ~+ b( y
(defun not (x)
4 y% y- D) a' V3 J* K, C) k (cond (x '())
" V! s5 _6 y, i1 ?5 A) F7 u" I ('t 't)))
. m0 v# |1 V z2 ~2 Q; ^/ c(defun append (x y)
" d/ z& T1 d6 V' M3 i$ A: G (cond ((null x) y)
+ ?. D9 Y9 Q2 G, D5 b0 _; T& T, |! K ('t (cons (car x) (append (cdr x) y))))) 8 M2 M: U. H% q' Z, D
(defun pair (x y)
: A4 J- j2 d+ y+ t+ m* g (cond ((and (null x) (null y)) '())
; p5 U8 W3 {; m0 Q- }& s3 }/ p ((and (not (atom x)) (not (atom y)))
" O f" M L8 C& O. S6 ~% B (cons (list (car x) (car y))
" g$ T$ \' j9 U+ N4 _ (pair (cdr) (cdr y)))))) 1 j7 [" A6 T( ?& Q
(defun assoc (x y)
! t6 M" [. V! H6 D+ p" Y7 M$ u (cond ((eq (caar y) x) (cadar y))" \: B7 J# ]+ G9 B- B. e: E5 S
('t (assoc x (cdr y)))))
2 ]' R7 K1 @# Z; E5 ~, l! F: u(defun subst (x y z)0 q' a8 q6 W/ R1 K. r
(cond ((atom z)2 ^* G4 h, u1 u
(cond ((eq z y) x)
5 N* B( T+ j, ^' R6 d( P0 A$ A ('t z)))
$ H4 \; `4 S& w+ X ('t (cons (subst x y (car z))
9 J5 ~4 [6 z- |7 N7 d. n0 u (subst x y (cdr z))))))
) v$ y, u2 Q' \( v如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
) e# k1 \$ \7 Y' Q) u1 D4 A: `: t7 g; o( h
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。
- n$ \3 W/ X) H; N4 P
0 F# h6 c" \2 ^; i- }理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |