我正在编写一个Lisp程序,并试图对类型有点认真。我想有性能改进,但我更感兴趣的是使用类型注释来提高文档和安全性。问题是nil
。到目前为止,我遇到了两个问题。
图表A:
>(defmethod foo ((bar bar-class) (quux quux-class))
...)
>(foo (make-instance 'bar-class) nil)
ERROR: No applicable method, etcetera etcetera, because nil is not of type quux-class
图表B:
(defmethod initialize-instance :after ((f foo) &rest args)
"Initialize the grid to be the right size, based on the height and width of the foo."
(declare (ignorable args))
(setf (slot-value f 'grid) (make-array (list (width f) (height f))
:element-type 'foo-component
:adjustable nil
:initial-element nil)))
style-warning:
NIL is not a FOO-COMPONENT.
这里的最佳做法是什么?到目前为止,我所拥有的唯一具有远程洞察力的想法是使用null object pattern并拥有(defclass nil-quux-class (quux-class) ...)
和(defclass nil-foo-component (foo-component) ...)
,但这似乎充其量只是hacky。我不确定为什么,但确实如此。坦率地说,我不习惯在CLOS中设计模式化的变通方法:)
答案 0 :(得分:5)
(A)当您使用foo
nil
致quux
时,您希望发生什么?
参数
如果你什么都不想发生那么
(defmethod foo ((bar bar-class) (quux null))
nil)
会把你排除在外。
如果你想要调用相同的代码,就好像你已经通过了一样
quux-class
的实例,然后是:
(defmethod foo ((bar bar-class) (quux quux-class))
(do-stuff bar quux))
(defmethod foo ((bar bar-class) (quux null))
(do-stuff bar quux))
或:
(defmethod foo ((bar bar-class) quux)
(unless (or (typep bar 'bar-class)
(null bar))
(error "Expected a bar-class or null, got ~s." quux))
(do-stuff bar quux))
(B)你走了
(make-array size :element-type 'foo-component
:initial-element nil)
并且你的lisp实现指出了一个矛盾 -
初始元素不能同时为nil
和foo-component
。 (好吧,我猜
这取决于您的类型foo-component
的样子。我在假设
它不包括null
。)
您可以考虑:
(make-array :element-type '(or foo-component null)
:initial-element nil)
但要注意:你希望你的口齿不清是通过了解一个来获得的
数组将包含foo-component
s或nil
s?优化?错误
检查你? (根据具体情况,您的里程可能会有所不同
你正在使用的lisp实现。)
答案 1 :(得分:4)
请注意,MAKE-ARRAY
的元素类型不是真正的类型声明。它提示了Lisp实现数组应该能够存储哪种数据。然后它可能会选择专门的数组实现。
UPGRADED-ARRAY-ELEMENT-TYPE
返回能够保存由typespec表示的类型的项目的最专业的数组表示的元素类型。
CL-USER 12 > (upgraded-array-element-type '(integer 0 100))
(UNSIGNED-BYTE 8)
上面意味着我请求一个数组,其整数元素在0到100之间。这个Lisp(这里是LispWorks)将给我一个元素类型为(unsigned-byte 8)
的数组。
更多例子:
CL-USER 13 > (upgraded-array-element-type 'fixnum)
(SIGNED-BYTE 64)
CL-USER 14 > (upgraded-array-element-type 'complex)
T
CL-USER 15 > (defclass foo-component () ())
#<STANDARD-CLASS FOO-COMPONENT 402030196B>
CL-USER 16 > (upgraded-array-element-type 'foo-component)
T
T
这意味着数组实际上将存储所有类型的数据对象。
CL-USER 17 > (upgraded-array-element-type '(or null foo-component))
T
CL-USER 20 > (make-array 2
:element-type 'foo-component
:initial-element (make-instance 'foo-component))
#(#<FOO-COMPONENT 40203B9A03> #<FOO-COMPONENT 40203B9A03>)
CL-USER 21 > (array-element-type *)
T
上面显示Lisp也会忘记最初请求的内容。我们实际上得到了一个元素类型T
的数组,当我们要求它的元素类型时,它是T
。