Skip to content

LISP - 结构体类型

Lisp - 使用 defstruct 定义结构类型

Section titled “Lisp - 使用 defstruct 定义结构类型”

在 Common Lisp 中,defstruct 是一个宏,用于定义简单、高效、类似记录的数据结构。它提供了一种便捷的方式,将相关数据分组到具有命名槽位(named slots)的单个对象中。defstruct 会自动为您的新数据类型生成构造函数、访问器和其他实用函数。

最简单的 defstruct 定义了一种新类型及其槽位。Lisp 随后会创建一套用于操作该类型的函数。

;; 定义一个名为 'book' 的结构,包含四个槽位。
(defstruct book
title
author
subject
id)
;; 创建 'book' 结构的一个实例。
;; 构造函数 'make-book' 会自动为我们创建。
;; 使用关键字参数是标准且易读的做法。
(let ((book1 (make-book :title "The Pragmatic Programmer"
:author "Andy Hunt & Dave Thomas"
:subject "Software Engineering"
:id 9780201616224)))
;; 使用自动生成的访问器访问槽位。
;; 访问器格式是 '结构名'-'槽位名'。
(print (book-title book1))
(print (book-author book1))
(print (book-id book1))
;; 类型谓词 'book-p' 也已创建。
(print (book-p book1)))
"The Pragmatic Programmer"
"Andy Hunt & Dave Thomas"
9780201616224
T

defstruct 具有高度可定制性。您可以为槽位定义默认值,并控制辅助函数的生成。

(defstruct user
(name "Anonymous") ; 'name' 槽位的默认值
(level 1) ; 'level' 的默认值
email)
;; 创建一个用户,只提供必需的 'email' 槽位。
(let ((new-user (make-user :email "guest@example.com")))
(print (user-name new-user)) ; 打印默认值
(print (user-level new-user)))
;; 创建另一个用户,覆盖默认值。
(let ((admin-user (make-user :name "Admin" :level 99 :email "admin@example.com")))
(print (user-name admin-user)))
"Anonymous"
1
"Admin"

结构通过 :include 选项支持单一继承。新结构会继承父结构的所有槽位。

(defstruct person name age)
;; 'student' 结构包含 'person',因此继承了它的槽位。
(defstruct (student (:include person))
major
gpa)
(let ((s1 (make-student :name "Jane Doe" :age 21 :major "Computer Science" :gpa 3.8)))
;; 我们可以使用父结构的访问器。
(print (student-name s1))
(print (student-major s1)))
"Jane Doe"
"Computer Science"

在 defstruct 和 defclass(来自 Common Lisp 对象系统,即 CLOS)之间进行选择是一个常见的设计决策:

  • 何时使用 defstruct:当您需要一个简单、轻量级的数据容器时。结构通常由 Lisp 系统更高效地实现,非常适合对性能敏感的代码,或者当您只需将少量数据捆绑在一起时。
  • 何时使用 defclass:当您需要面向对象编程的全部功能时。这包括多重继承、方法特化(多态性)和类的动态重新定义等,这些特性在结构中是不可用的。

可以将 defstruct 视为用于“数据对象”,而 defclass 用于“行为对象”。

defstruct 是 Lisp 程序员工具箱中一个实用且必不可少的工具。它提供了一种简单、高效、清晰的方式来定义用于聚合信息的自定义数据类型,在原始列表和功能齐全的 CLOS 之间取得了完美的平衡。