Lisp的语法元素在前几集中已经基本讨论完毕,相比C#或Java数百页的Specification,它可能简单的让你有些惊讶,不过,伟大的东西总是简单的,不是吗?现在让我们来回顾一下上一集中提到的内容,首先提几个问题:3 k7 Y+ |/ J) z$ ]
2 ]* I2 m5 R+ s$ c6 e
既然cond在概念上相当于过程式语言中的if语句,那么与if相对的else分支在cond表达式中应该如何描述? 1 k. a! q3 X* N4 f
在(我们已经学过的)Lisp中如何表达“重复”这个语义?或者你能写一个foreach循环函数?# M. c/ g# G, U. j7 Z) H* e
(注:不要问输入输出函数或算术逻辑运算在哪儿之类的问题,它们都是微不足道的事……)- R; R- X5 ?0 M; D) V% H
8 s6 S/ _. {$ e' G% G: X这一集中,我们将描述几个常用的函数,并给出它们的简单实现
0 R$ F) Q Q, X% K
C O( F' B4 }. v首先解答在第一集中提出的问题:如何取一个表中的第二个、第三个或第n个元素?; Z" Q s6 U5 e# H3 h
0 Y2 t+ w) y* q/ s/ V/ @' Y1 ]0 G可能有些读者已经想到了,取第二个元素可以采用如下形式:
2 r4 ~, _4 A7 _' x, p t% Y/ Y1 q6 O, B: y/ d% m
(car (cdr x))7 O- l! A$ Y7 g* l+ ]& y' n
9 n0 S' H0 u8 L6 m g2 d
同理,取第三个元素是这样的:
3 j" Z( \# n. c
% ?5 D5 f2 _" u' }) N. x(car (cdr (cdr x)))
: y) r& [/ {* L4 A
2 e( ^, v: M) y事实上,这种组合在Lisp中经常要用到,为了方便,Lisp提供了一个通用模式——cxr,其中x为a或d的序列,来简记car和cdr的组合,例如:' X' Q) i+ r, C. y# v9 m
# k* z% O" O; m3 w; v7 I& `+ A> (cadr '((a b) (c d) e))
' b+ U6 i% {( Q6 q% K(c d)
/ X- M Q( W' Y$ `% K* z> (caddr '((a b) (c d) e))' k, B3 B& p1 E. k! J' [ O
e% O1 ~7 T0 U, x( P# o
> (cdar '((a b) (c d) e))' F" q Z0 O" b3 L
(b)- |# V, T/ b: D; s, q
9 D! [+ q X( `' b W- }& }# a' T% B另外,使用(list e1 e2 ... en)来表示* [( }3 ]$ `! g" m* H+ t* b- t: j
(cons e1 (cons e2 (... (cons en '())...)))2 ?1 f$ H% W& N' J" D7 y
5 ]- X4 ?/ k/ N- _+ |
> (cons 'a (cons 'b (cons 'c '())))
+ V$ p2 y( J0 A& b& L+ E, a(a b c)8 I5 H, I' @9 V+ ]- W. X
> (list 'a 'b 'c). T, [4 O9 ^; d
(a b c)
9 }0 H+ ], S! E- e$ J6 ?8 z3 }4 J
现在我们定义一些新的常用函数,我建议你先自己想一想,不要急着看我给出的实现。" p6 I# @4 z* b. E, j' R9 D# n
, D# f5 ]$ ^+ F1 \7 b& m
(注:某些函数在Common Lisp中已经存在,所以如果你想试验一下,给它们换个名字)( Z0 w8 [2 C5 n2 f: g0 V
7 b9 }7 e! F- {% Z5 {(null x),测试x是否为空表。例如:% ]* D0 |" h( K. d( K) {( V# b
> (null 'a)1 y' X2 r" Y* U% [$ {* i+ ?
()
4 J) l: `9 j5 ~7 O1 p9 D0 _& l" Z. W> (null '())
/ T& s/ I- I* P5 ^6 dt / W/ c% |0 K3 b5 Z& o# `
(and x y),逻辑与,当且仅当x和y都不是空表时返回't,否则返回空表。* Z \# i* |- Y$ P- R
> (and 'a 'b)
5 x) c4 ]" ^9 I& B$ dt# `9 g' z( i. h3 s
> (and (atom 'a) (eq 'b 'c))5 P- y+ u: d9 W+ L5 _9 k2 Q& j6 H. s
()
8 f( \: P* r4 q: M v(not x),逻辑非,当x是空表时返回't,否则返回空表。(有人问我or在哪儿?)例如:
( A y' m( @7 d6 T' f* f q' t> (not 'a) Y3 O5 Q( J! a) b# s( V
(), O9 l( [5 ] O
> (not (eq 'a 'b))1 y' C3 |, L1 {: B* p# N' n; h
t
, E2 V) m2 {- Y(append x y),连接两个表x和y,注意它与cons和list之间的不同之处。例如:2 N3 }6 t* }. t. i
> (append '(a b) '(c d))
# p( b5 H# J' j( k/ ?: Z(a b c d)
) X7 \ ^+ y( E2 Z8 B5 u0 ]> (append '() '(x y))
/ j) c6 p$ u$ H% \9 L, Z0 ^(x y) " y3 @1 z7 ~) M7 i6 i2 q6 r; S
(pair x y),这里x和y是两个长度相同的表,pair生成一个表,其中每个元素是x和y中相应位置上的元素组成的一个元素对,这个函数的返回值类似于其它语言中的map或dictionary的概念。例如:+ W5 u' a) a) i/ [. V
> (pair '(a b c) '(x y z))$ @6 _: W9 r x5 N; {
((a x) (b y) (c z)) , }, h0 Q& q. q& m
(assoc x y),其中x是一个原子,y是一个形如pair所返回的表,assoc在y中查找第一个左元素为x的元素对并返回。例如:
9 ]: N- {% j# F& @6 _+ I> (assoc 'a '((a x) (b y)))
8 J3 B, g0 T! z+ f" xx
7 d& R2 f/ } ^/ T# o> (assoc 'a '((a (foo bar)) (b y) (c z))). @0 C8 |9 j, T9 j- d2 f% e7 V
(foo bar)
) ^! a0 Z- u7 }3 w. y. W(subst x y z),在表z中将任意层次上出现的原子y都替换为表达式x。例如:
0 S$ L- T9 {8 l; G> (subst '(x y) 'b '(a b (a b c) d))
- [" y" T( z3 M8 C(a (x y) (a (x y) c) d)
- o0 i$ w7 W# h) E下面我们给出这些常用函数的简单实现:
! K8 w+ B1 P( o' t' }+ @) q
3 {& y( Q" {9 c4 k' }(defun null (x)
# s c: d! u+ h+ s: ^1 | (eq x '()))
$ H0 T& ?; t+ R9 o% H" a(defun and (x y)
/ c: o5 ^ J6 p- {/ ? (cond (x (cond (y 't) ('t '()))); k2 N1 c& }2 r9 P9 b- z+ l
('t '()))) 9 N9 ~8 E$ f+ m0 e6 L: t8 ~( a, W! {. U
(defun not (x)9 j8 ] ]4 \& ]. l9 n! ?
(cond (x '())
v1 w$ T4 a# [ ('t 't)))
1 u* L7 Z' C i. V1 a(defun append (x y)( Z( e W5 x% t0 ?$ f# Y
(cond ((null x) y)
! q. p7 ^! n4 J! i: [ ('t (cons (car x) (append (cdr x) y)))))
1 m+ v; D! Z. R8 k( I, _5 A/ W(defun pair (x y)7 J( G5 V& p/ w% `! w6 k& O- P# O. V
(cond ((and (null x) (null y)) '())
$ i7 P; D% y- E/ O ((and (not (atom x)) (not (atom y)))
# |# e y. l( k8 @ M( i (cons (list (car x) (car y))+ C: W: j8 t; X/ o1 G
(pair (cdr) (cdr y)))))) 1 |; J* J3 s. Q' C2 n$ x6 Q
(defun assoc (x y)3 t0 y8 S! j S1 t6 U3 w) L2 d$ S
(cond ((eq (caar y) x) (cadar y)): Z" k# B$ t& |' l% P! R' S
('t (assoc x (cdr y)))))
3 ~+ J: H" l9 [) g% {3 I3 r- K B(defun subst (x y z)1 z# G- `& @9 ^. X7 ]
(cond ((atom z)
) Q' |2 V0 m2 Y (cond ((eq z y) x)5 L5 g3 }$ M1 D9 ^" m. n
('t z)))9 x. X% E' j' r/ V K
('t (cons (subst x y (car z))
1 n$ G I0 A8 [# \; w# j* n$ L0 P* K (subst x y (cdr z))))))
; ^9 R1 A5 x% n+ |9 F6 \, o如果看到这里你还没有晕菜,说明你的神经的确很坚强。注意在这些例子中是如何表达“重复”这个概念的,在Lisp中,最常用的重复其实并不是真正意义上的重复,而是递归,这也是绝大多数函数式语言的一个共同特征——函数的嵌套和递归,构成了整个程序逻辑。, E' w; `/ F7 D/ ^" H
! k3 X6 d+ E( I& n% W: H
这一部分内容可以让你真正感受到Lisp的特色,与编写过程式语言的程序相比,编写Lisp程序需要一种完全不同的思维方式,也许这正是Lisp语言几十年来长盛不衰的真正原因吧。, `4 V, v( d) G- k, J& ^. t
) l) h1 V7 A& e; T1 C3 B! C/ S
理解了这一部分,下一集中我们将领教一下Lisp的威力,我们将用Lisp编写一个Lisp解释器。如果你以前没有见过这个程序,我保证它一定会让你吃惊。 |