Skip to content

LISP - 结构体选项

Lisp - 使用 defstruct 选项自定义结构体

Section titled “Lisp - 使用 defstruct 选项自定义结构体”

The defstruct 宏是创建自定义数据结构的强大工具。除了定义简单的槽位(slot)之外,它还提供了多种选项来控制结构体的行为,例如如何创建、打印和识别它。

默认情况下,defstruct 会创建一个名为 make-<struct-name> 的构造函数。:constructor 选项允许您自定义此行为。

  • (:constructor): 阻止创建任何构造函数。
  • (:constructor make-my-point): 将构造函数重命名为 make-my-point。
  • (:constructor make-point (x y)): 创建一个按位置而非关键字接受参数的构造函数。这对于为小型、常用结构体创建简洁的构造函数很有用。
;;; 定义一个带有自定义位置构造函数的点结构体
(defstruct (point (:constructor make-point (x y)))
x
y)
;; 使用自定义构造函数创建一个实例
(let ((p1 (make-point 10 20)))
(format t "Created point with X=~d, Y=~d~%" (point-x p1) (point-y p1)))
Created point with X=10, Y=20

这些选项控制类型检查谓词和结构体复制函数(copier function)的命名。

  • :predicate: 默认谓词是 <struct-name>-p。您可以使用 (:predicate is-a-point) 重命名它。使用 (:predicate) 来禁用它。
  • :copier: 默认的复制器是 copy-<struct-name>。您可以使用 (:copier duplicate-point) 重命名它。使用 (:copier) 来禁用它。
(defstruct (user (:predicate userp) (:copier nil)))
name
id)
(let ((u1 (make-user :name "Alice" :id 101)))
(format t "Is u1 a user? ~a~%" (userp u1))
;; (copy-user u1) 会导致错误,因为复制器已被禁用。

The :print-object 选项(:print-function 的一个更现代、更用户友好的替代方案)允许您定义一个自定义函数,用于控制 print、format 等如何显示您的结构体实例。这对于调试和创建可读输出非常宝贵。

;; 首先,定义负责打印的函数。
;; 它必须接受两个参数:对象和输出流。
(defun print-custom-point (point stream)
(format stream "<Point (~a, ~a)>" (point-x point) (point-y point)))
;; 现在,定义结构体并将其与我们的打印函数关联。
(defstruct (point (:constructor make-point (x y))
(:print-object print-custom-point))
x
y)
(let ((p1 (make-point 10 20)))
;; 现在 `print` 和 `format` 将自动使用我们的自定义函数。
(format t "The point is: ~a~%" p1))
The point is: <Point (10, 20)>

The :include 选项提供简单的单一继承,允许一个结构体继承另一个结构体的槽位。这是减少代码重复的好方法。

;; 基础结构体
(defstruct shape
(color "black"))
;; Circle 继承自 shape 并添加了半径
(defstruct (circle (:include shape))
radius)
(let ((c1 (make-circle :radius 10 :color "red")))
;; 两个结构体的访问器都有效
(format t "Circle color: ~a, radius: ~a~%" (circle-color c1) (circle-radius c1))
(format t "The circle is: ~s~%" c1))
Circle color: red, radius: 10
The circle is: #S(CIRCLE :COLOR "red" :RADIUS 10)

默认情况下,结构体可以实现为列表或某些特定于实现的方式。:type 选项允许您强制底层表示为 list 或 vector。这对于性能调优或与其他代码进行接口非常有用。

;; 定义一个由向量支持的点结构体
(defstruct (vec-point (:type vector))
x y)
(let ((vp1 (make-vec-point :x 5 :y 15)))
(format t "Type of vector-point: ~a~%" (type-of vp1)))

输出将是一个向量类型,可能如下所示(具体输出可能因实现而异):

Type of vector-point: (SIMPLE-VECTOR 2)

结合这些选项可以创建高度专业化和健壮的数据类型。对于涉及多重继承和方法分派的复杂面向对象编程,请考虑使用 Common Lisp 对象系统 (CLOS) 和 defclass,defstruct 就是基于它构建的。