用 NIL 表示 null 元素并没有错,但如果你真的想强制类型,你可以按如下方式进行。
ALLOCATE-INSTANCE 泛型函数(但显然不是INITIALIZE-INSTANCE)被指定用于STRUCTURE-CLASS 的实例。
函数空元素的前向声明
此函数返回类型 element 的空值。
使用 SBCL,如果您一次编译所有定义(即使用 compile-file),则不是绝对必要的。
(declaim (ftype function null-element))
定义结构
values 的 initform 是对null-element 的调用,以便在形成新结构时该值满足类型。没有为此结构定义任何构造函数,以使手动构建它变得更加困难(并避免命名问题)。
(defstruct (element (:constructor nil))
(value 0 :type fixnum)
(next (null-element) :type element)
(prev (null-element) :type element))
实例化一个元素
这里的代码分配结构,但不初始化它。这避免了 sbcl 可能遇到的问题,它会自动检查其插槽的类型。这里,槽的内容在分配后是未定义的,但随后它们被设置为适当类型的值(元素本身)。
(defun make-element (value)
(let ((element (allocate-instance (find-class 'element))))
(prog1 element
(setf (element-value element) value
(element-next element) element
(element-prev element) element))))
使用一个类,我会调用initialize-instance,但我认为这并不适用于所有实现。使用setf直接设置槽位即可。
在这里,我同意您的问题,并让元素具有指向自身的链接。
或者更好:有没有办法通过 next 和 prev 引用实例本身来创建起始实例?
您也可以将字段设为(null-element),但这只是设计选择的问题。
单例空元素值
调用(null-element) 返回一个代表空元素的标记值。在这里,我在加载时分配一个element 实例,并在每次调用时返回相同的实例。存储在节点中的实际值并不重要。正如 RainerJoswig 所指出的,null 元素可能是没有此类值的结构的实例。
(defun null-element ()
(load-time-value (make-element 0)))
(defun null-element-p (element)
(eq element (null-element)))