【翻译】Lisp.结构(structure)

5 views
Skip to first unread message

panfei

unread,
Oct 17, 2012, 3:17:01 AM10/17/12
to lisp-...@googlegroups.com

一个结构(structure)可以看成是一个加强版的向量(vector)。假设你需要写一个程序来跟踪许多长方体。你可能会考虑将它们表示成一个具有三个元素的向量:高,宽和长。如果你使用诸如如下的函数,而不是使用原始的svrefs,将使你的程序更加易读:

[plain] view plaincopy
  1. [1]> (defun block-height (b) (svref b 0))  
  2. BLOCK-HEIGHT  

你可以将一个结构看成是一个特殊的向量,类似于以上的这种函数都给你定义好了。


要定义一个结构,我们要使用defstructure。最简单的情况下,我们只给出结构的名字和和字段:

[plain] view plaincopy
  1. [3]> (defstruct point x y)  
  2. POINT  

这把一个“点”定义成一个结构,这个结构有两个字段,x和y。它也隐式地定义了如下函数:make-point, point-p, copy-point,point-x和point-y。我们曾经提到过Lisp程序可以写Lisp程序。这是到目前为止我们看到的最显著的例子了。当你调用defstructure的时候,它自动地书写了定义其它几个函数的代码。利用宏你可以做到同样的事情。(如果需要,你甚至可以自己版本的defstructure)


对make-point的调用会返回一个新的点。我可以通过给出相应的关键字参数来设定单独的字段:

[plain] view plaincopy
  1. [2]> (setf p (make-point :x 0 :y 0))  
  2. #S(POINT :X 0 :Y 0)  
“点”的字段访问函数不仅仅能用来取值,也是可以和setf来共同工作的。

[plain] view plaincopy
  1. [3]> (point-x p)  
  2. 0  
  3. [4]> (setf (point-y p) 2)  
  4. 2  
  5. [5]> p  
  6. #S(POINT :X 0 :Y 2)  

定义一个结构也同时定义了一个相同名字的类型(type)。每个点都是“点”(point)类型,然后是结构(structure),然后是原子(atom),然后是t。同样我们使用point-p来判断一个东西是不是一个“点”,当然我们也可以使用通用的函数,像是typep。

[plain] view plaincopy
  1. [11]> (point-p p)       
  2. T  
  3. [12]> (typep p 'point)  
  4. T  
我们可以为结构的字段设定默认值,方法就是在原始定义中将字段名和一个默认表值达式。

[plain] view plaincopy
  1. [1]> (defstruct polemic  
  2.   (type (progn  
  3.           (format t "What kind of polemic was it ?")  
  4.           (read)))  
  5.   (effect nil))  
  6. POLEMIC  
  7. [2]> (make-polemic :effect "good")  
  8. What kind of polemic was it ?8  
  9. #S(POLEMIC :TYPE 8 :EFFECT "good")  
[plain] view plaincopy
  1. [5]> (make-polemic)               
  2. What kind of polemic was it ?8  
  3. #S(POLEMIC :TYPE 8 :EFFECT NIL)  
如果对make-polemic的调用没有指定这些字段的默认值,它们会被设定成对应的表达式。


我们也可以控制结构显示的方式,还有自动创建的访问函数的名字中使用的前缀。这里是做以上两件事情的一个详尽的定义:

[plain] view plaincopy
  1. [6]> (defstruct (point (:conc-name p) (:print-function print-point)) (x 0) (y 0))  
  2. POINT  
  3. [7]> (defun print-point (p stream depth) (format stream "#<~A, ~A>" (px p) (py p)))  
  4. PRINT-POINT  

:conc-name参数指定了访问函数中加在字段名之前的前缀。默认是point-;现在成了简单的p了。不使用默认的前缀使得你的代码损失一些易读性,所以你只有在经常使用访问函数的时候才希望这样做。


:print-function是被用来在需要打印“点”的时候,使用的函数的名字——比如toplevel。这个函数必须接受3个参数:需要被打印的结构,打印到哪里,第三个参数通常忽略掉。我们将会在后面学习到streams。现在来说,知道stream可以简单地传递给format就足够了。


函数print-point将以简短的形式来打印点:

[plain] view plaincopy
  1. [8]> (make-point )  

--
不学习,不知道

Reply all
Reply to author
Forward
0 new messages