Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:$ T0 Q& M5 Q/ J X
0 o/ J# n) U7 X/ h! F5 O% c: b! w
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? 8 a# G" p2 Z4 o2 c+ u( m! v9 ]# W( H
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?' P; Q9 M! p! x* ? ]. h. `( E
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)/ [' V- x% W2 j. i
* L z" n! u7 E/ l, S
这一集中,我们将描述几个常用的函数,并给出它们的简单实现
/ A/ P% n( C4 D) Q8 q$ G+ {3 X. l3 Y6 T. h/ e+ q$ _! e& x
首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?
. \, m- a) Y( H8 H- k8 f* @( ^+ q9 {$ W& [* S7 y8 q! D
可能有些读者已经想到了,取第二个元素可以采用如下形式:: m5 t3 ~1 K; M
) h0 d. R+ M2 B0 o- `1 m3 l4 o(car (cdr x))
/ y: D1 u) w; k W' o' a, G# e6 B% l5 J l7 c; y
同理,取第三个元素是这样的:- k( Y: B; I0 I* F! B
7 u# k4 u: M. o3 ]+ \$ h
(car (cdr (cdr x)))
- a0 N# v* v2 N/ s. t7 D
' n4 B1 J: T4 s, K事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
1 L1 H+ x/ {: N, E6 ?5 S8 [! e4 g' I& l
> (cadr '((a b) (c d) e))
+ L: g- N. r9 s# X/ ~(c d)
/ @* j# f* J! S* ?; k> (caddr '((a b) (c d) e))
5 A* h# M& [& k& A, s% }" g- N! Le
2 G5 p. x5 c0 C+ |> (cdar '((a b) (c d) e))3 `! Q( v1 H+ A/ v: Z/ Z+ }
(b)' G. R/ i V2 Y9 e. O: m
4 r7 V0 q& u" T( r+ G* r) H& T另外,使用(list e1 e2 ... en)来表示
+ @$ u q( y0 v# I; Q; H6 L8 Z(cons e1 (cons e2 (... (cons en '())...)))5 ?5 p' h# }. n3 k! @ a8 N
, w. {0 H- J+ j0 p4 b7 m: M> (cons 'a (cons 'b (cons 'c '())))
) q2 R! w( p$ A* F' G) r. Q(a b c)
: r, Q2 o: f3 Z* J$ b; X! |> (list 'a 'b 'c)
: P% L N: f5 H; L: Y( S) U$ T(a b c), u4 e% g" f- n) h, w5 ~- ~% r8 R
( a ]/ I0 Y' {& ^
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。2 I4 r6 g& i4 B$ i1 e) G% m( N
4 \' Y& v v0 f/ K1 e(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)# f Q$ N4 V& P. r, a
& B+ J5 }6 U/ ^ W(null x),测试x是否为空表。例如:8 b* L0 s/ Q7 N. n9 D+ ^# [' f4 V* O
> (null 'a)
2 u. O- I, @; b$ Q3 {()$ p3 ?5 q+ _8 Q7 L" W
> (null '())) v- g6 Z! J8 ^: p+ v9 y
t ! K- s2 \& |$ X5 s6 W* `1 k
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。
. e" A0 R: G& X4 d0 g* y% b2 O> (and 'a 'b)# h0 C& N8 E4 A
t# b9 ], M: |0 s
> (and (atom 'a) (eq 'b 'c)) V3 i J! X7 `7 f+ J/ |+ O% Q
() / p% ]/ W5 A) j+ f, i3 E2 p, U
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:- a3 W3 i/ V' T
> (not 'a)! I5 K& \! v6 p' @0 u( h4 I* x3 R
()0 E8 Z( n) F( R8 [) f2 \
> (not (eq 'a 'b))8 S" Q* J" H: v" x, B
t
* b* R( b& Q! a' ~, h: V8 x1 @(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:
@1 H: Y2 M7 {6 ?& b% K8 M> (append '(a b) '(c d))
% e. ]; ^3 K. x- A(a b c d)6 b/ [) y; r4 s* L! z+ t( q
> (append '() '(x y))9 \9 y4 X3 A3 ^9 X
(x y)
. E* X, v8 u, X9 K* T(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:
) |6 N! I0 @2 q> (pair '(a b c) '(x y z)), i) P$ f/ W) p* \- i, H$ U
((a x) (b y) (c z)) 0 H& D( B7 R# ~" T5 T
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:
* ~" M4 [7 t* }; {; Q3 f* N> (assoc 'a '((a x) (b y)))" b6 |! N- a/ F4 s) ^0 s) }- V& G
x9 c& G- y/ P6 W* z
> (assoc 'a '((a (foo bar)) (b y) (c z)))
) D& @: G: X4 n5 @: M1 c( w* ^& A(foo bar) 1 S. A& M! }8 E) X2 Y) R
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:0 D U3 ~" h& t: p: G. `+ V
> (subst '(x y) 'b '(a b (a b c) d))
9 _0 s% U L$ c$ y) j" D(a (x y) (a (x y) c) d)6 F9 R/ E0 g1 P; V ^
下面我们给出这些常用函数的简单实现:
3 R4 _9 R- D$ T+ G0 M- y9 }. ?, W4 G) _
(defun null (x)6 L7 H$ ]+ F: ]& X5 q3 P: ] w
(eq x '()))
2 c6 o1 ^7 S7 g& @(defun and (x y)
5 J( N0 j1 ^. k7 W! f (cond (x (cond (y 't) ('t '())))
% i) K! k& Q' u$ t ('t '()))) 6 n P2 R9 b4 w5 h7 p! E
(defun not (x)) O2 a, \0 ]$ t7 s, X4 T9 H
(cond (x '())
. ?; ]7 g% U) ?7 \ ('t 't))) 4 E* P) o c5 ^/ D9 M2 O
(defun append (x y)2 V9 k, K6 l4 ^
(cond ((null x) y)
& R& G' O* z" d, _: Z" l ('t (cons (car x) (append (cdr x) y))))) - U2 P! K8 e- \2 M9 {. G
(defun pair (x y): K, {6 M% Y8 T! \
(cond ((and (null x) (null y)) '())
( U, c/ ^( |1 D' H; L( {0 Y ((and (not (atom x)) (not (atom y)))
* H: T; w( q, z( `7 Y6 K+ J r1 |" U (cons (list (car x) (car y))
) G* k% P# K2 k8 X6 A9 @3 s (pair (cdr) (cdr y))))))
. ~) W- N- Y5 g5 M% s9 K* G; J(defun assoc (x y)5 H( C% }- D( P( R; K, m0 g* E
(cond ((eq (caar y) x) (cadar y))
% E! X2 k1 f0 a ('t (assoc x (cdr y)))))
7 j4 r6 @! T* _(defun subst (x y z)
- c2 G; F* E5 Y* F# ]8 h' d (cond ((atom z)
/ m ~+ G1 w$ R% R! r (cond ((eq z y) x), ~( W3 n( g8 q
('t z)))
/ m3 M! |4 l/ }1 A* b ('t (cons (subst x y (car z))8 S$ A3 B& d; }, _
(subst x y (cdr z))))))
" S! ?" `$ b2 _- [. L0 [如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。% }0 l8 M0 J- Q
/ }6 i+ ^% G4 I J$ r5 v% G U
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。6 |, w6 C6 [; ~
3 s6 j. E8 ?# o理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |