Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:
4 l' r$ X( k& [+ ~
8 m" i/ K. ^6 G% J$ x既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? 3 C5 o; Q" u# |1 V
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?
" O' P6 g( R4 G4 M9 o(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)
+ N4 m# Y4 i3 d" A- F
' W3 p2 U0 w( ^1 x5 P# `这一集中,我们将描述几个常用的函数,并给出它们的简单实现' V# l$ U/ X! e% C7 c
! h$ m9 M# S) T i) z% G2 j首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?1 C; V: z$ L, x# [
& q) ~5 G" X, i2 m) G4 s- V
可能有些读者已经想到了,取第二个元素可以采用如下形式:
" P1 P' e* U8 \6 f6 f2 ?: t; k
, c* P" S1 V B8 q" D1 T(car (cdr x))
- K$ D! y" L1 `1 H& e7 d5 f( X- ~7 s
同理,取第三个元素是这样的:/ A% v/ Z" c: k) M! v- P- ~
! _1 J( [2 ~+ A! p1 m
(car (cdr (cdr x)))
. b# ?0 H, R2 A+ ?* S/ `
# k$ y+ X4 H( y$ J1 ^# F5 a事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:
0 i5 r) V' P+ j7 n& Z1 N" F& F2 G8 q2 c# S
> (cadr '((a b) (c d) e))- q1 O2 v% p- p0 I1 ~
(c d)
- k* M' N, N) M S, f/ M> (caddr '((a b) (c d) e))
: F7 x3 k5 M" G/ z3 g4 |5 Be, U4 L4 H7 U. S% q* ?# Q
> (cdar '((a b) (c d) e))
4 f" q" L6 _% E; X8 M, a- v% J(b)2 \. h$ G$ N( p, `
9 R1 U: H0 \8 c
另外,使用(list e1 e2 ... en)来表示% l/ t; Z3 B! x1 c2 y- l1 k
(cons e1 (cons e2 (... (cons en '())...)))
& @( z$ {# E; F$ U t- z+ p; o; x' x& a8 k
> (cons 'a (cons 'b (cons 'c '())))' Y- q/ N, e% G( i1 _# b ^
(a b c)* Z1 z4 ~; D6 a3 g1 m0 j: Q
> (list 'a 'b 'c); f5 Z' V& B: o u% c. ^. ]
(a b c)
" d! F3 N: k& J+ M8 B& `7 y6 q7 b# `0 ]3 {
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。# H# P; [# O0 z
9 b& u$ q }6 N(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)
0 O' d4 e& @& T' C+ O' A- K1 j) C, ]+ b- c1 J1 f
(null x),测试x是否为空表。例如:) N6 k' M% ^8 b, i0 \3 Z3 B
> (null 'a)
* Z. O: @( G# R()
; z+ V- }6 I+ ~3 l9 P> (null '())0 o& y7 r2 ]0 `2 d2 H
t 5 |% ]7 K. {8 z0 l7 N( x" q
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。9 C" a+ j' @# r! p* Y
> (and 'a 'b)
' E0 C. k; S- C6 o. _. n2 yt' b9 Q; k1 n! ?# }8 d0 j( Y
> (and (atom 'a) (eq 'b 'c))
, j6 b6 k# b' Z4 e( G7 n0 i() : S' R/ w/ M1 u8 o& v7 P
(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:
9 s1 U; x v8 F> (not 'a)% F' {5 P/ l( G$ c! l% _7 j
()
$ R1 x7 v$ j# i, K> (not (eq 'a 'b))9 {+ }' A8 g/ R1 F) u+ }: u' _- U
t - B, ?" b0 ~, B
(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:* b* k" n$ F" |. C
> (append '(a b) '(c d))
& [, B6 y, s5 F8 j7 o1 m(a b c d)
! R# l( Z6 o5 L8 O> (append '() '(x y)); U5 v% L' K4 }$ Z' P$ x3 W
(x y)
2 l4 z* U3 O) Z2 z3 R' P9 {(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:6 H0 o$ Z* B+ x
> (pair '(a b c) '(x y z)): S5 x7 w1 { ^6 g1 e5 J2 G, R
((a x) (b y) (c z)) + M3 }* _# `/ Y3 U% J2 W# }- ^
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:& {( }0 |( B/ }# t- |, g0 v
> (assoc 'a '((a x) (b y)))6 I, P0 D: i, Z0 q# P- }
x( Q- J$ n2 ~9 k+ ]0 J+ x4 o
> (assoc 'a '((a (foo bar)) (b y) (c z)))1 f# t" l9 h5 w% F* h) ^- y: p
(foo bar) ; Y3 ]* Y0 V# Z$ [
(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:$ a; U+ C9 @+ Q& _+ @6 ?
> (subst '(x y) 'b '(a b (a b c) d))
9 q7 X& P. }* Q; e(a (x y) (a (x y) c) d)# E2 @, @7 Z9 L/ g
下面我们给出这些常用函数的简单实现:0 i f% R2 x1 v$ f7 }% n
\) x N" K9 y/ M. M( Q6 J1 }8 C; G(defun null (x), @# _( h+ R& Z) c, v3 Y
(eq x '()))
% k- u4 a9 c g+ {3 ?1 @. C3 z& F$ S(defun and (x y); H- M y; i' w9 D" k. x9 }' X6 D
(cond (x (cond (y 't) ('t '())))8 Y P2 \$ w# Q) X4 j* F4 d9 [
('t '())))
1 I( j- ]3 t9 |8 ?0 W& C" O: r(defun not (x)# v3 x% M8 o. l
(cond (x '())3 K' I8 `1 \" @5 h
('t 't))) ! e8 O' n' r$ y4 N: M& p
(defun append (x y)1 I$ ?& {2 D2 Z7 U3 c2 s
(cond ((null x) y)8 j* d9 P/ ]4 e
('t (cons (car x) (append (cdr x) y))))) . d* V0 I5 y: O% h W# K' w, i
(defun pair (x y)2 ^: G6 Z4 y0 N2 z( j' p
(cond ((and (null x) (null y)) '()); K) l8 }( A( r8 |: t6 W1 c, |
((and (not (atom x)) (not (atom y)))
9 R2 C7 F3 }1 t$ a7 o" {+ _1 I! @ (cons (list (car x) (car y))* U5 @- o- o7 h6 D5 m
(pair (cdr) (cdr y))))))
& y; K0 z* j& Q6 z8 |(defun assoc (x y) @- x: |) r. m
(cond ((eq (caar y) x) (cadar y))8 f0 ?0 J$ S2 b/ V0 O6 x
('t (assoc x (cdr y))))) ( {9 x7 o" q8 n: H8 {7 r
(defun subst (x y z)
! g$ @% t7 ?* s; c5 I (cond ((atom z), V; o4 [( |# [ a/ \: F
(cond ((eq z y) x)
4 S) ]# w# f4 S7 r ('t z)))6 s! ^5 T9 m7 x$ i: }9 @
('t (cons (subst x y (car z))
: R# b4 ~& Q8 ?6 A2 w( ?% x (subst x y (cdr z))))))1 z/ A# C& H2 p+ M: ?0 \# ]
如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。
9 I8 j1 T% A( w, O! Q4 e, U
H6 s2 [) s/ g4 t+ L! Z$ ^: v! q" D这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。
v8 z& f6 `3 s9 d: s
3 a1 |6 e5 l) r/ @( b理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |