在 Common Lisp 的插槽说明符中使用和不使用 `:` 是否有技术原因?或者它只是一个纯粹的惯例?
Is there a technical reason for the use and absence of `:` in Common Lisp's slot specifiers? Or is it just a pure convention?
我正在阅读 Sonja Keene 的书 Common Lisp 中的面向对象编程。
在第 9 章中,作者给出了以下示例:
(defclass window ()
((x :initarg :x-position :accessor x-position)
(y :initarg :y-position :accessor y-position)
(height :initarg :height :accessor window-height)
(width :initarg :width :accessor window-width)
(exposed-p :initform nil :accessor exposed-p))
(:documentation "Foundation of all windows"))
我知道在谈论 packages 时,:
、::
的使用或它的缺失超出了 Common Lisp 中的纯符号。 其使用背后有一个逻辑!
但是,我看不出在插槽 options/specifiers 上使用 :
背后的逻辑。例如,考虑上面的例子。
看第一个插槽,x
。存在与 :x-position
关联的插槽选项 :initarg
。好的,两者都包括 :
.
现在,在同一个插槽上,查看 :accessor
插槽选项。它包括双点 :
。但是,与之关联的 x-position
不 包括 :
.
这个语法背后有什么逻辑吗?
我最好的假设是语法设计是用来反映处理实例时的差异。因此,没有双点表示使用与 pos-initialization 相关,包含双点表示正在进行的初始化过程:
CL-USER> (x-position (make-instance 'window :x-position 3))
3
还有什么事吗?我错过了什么?是纯粹的语法约定,我说的只是巧合吗?
谢谢
:accessor
选项用于定义方法名。函数名称应始终位于应用程序的包中,这样您就不会在定义同名插槽的两个包之间发生冲突。
如果你使用关键字:x-position
,而其他一些包做同样的事情,它们会相互冲突。
记住,单个冒号表示符号 :bar
- 是关键字
- 并且在关键字包中
- 并从关键字包中导出
- 并且具有自身的常量值 (->
:bar
)。
尝试
(defclass foo ()
((x
:initarg x-position
:accessor x-position)))
* (make-instance 'foo 'x-position 3)
#<FOO {7007741DD3}>
* (x-position *)
3
和
(defclass foo ()
((x
:initarg x-position
:accessor :x-position)))
* (make-instance 'foo 'x-position 4)
#<FOO {700778D473}>
* (:x-position *)
4
初始化参数是函数make-instance
(及相关)的关键字参数。访问器是函数的名称和setf 访问器的名称.
可以使 initarg 成为任何符号,而不是关键字符号。它只是一个普通的关键字,因为函数关键字通常带有关键字符号。
也可以尝试使访问器名称成为一个符号,而不是具体的关键字符号。一个实现可能支持也可能不支持。这意味着实现需要支持关键字作为函数名称。
风格与惯例
通常我们把initarg选项写成关键字符号,accessor选项写成非关键字符号。
正如 Barmar 所解释的那样,使用关键字符号定义访问器函数将是冲突的根源。访问器函数是通用函数,因此在不同情况下重复使用相同的符号似乎仍然有效。例如,两个不同的不相关的 classes c1
或 c2
将 :x-position
定义为访问器可以如下使用,对于 [=36 的任何对象 o
=] c1
或 c2
:
(:x-position o)
唯一通用函数 :x-position
的动态调度会起作用。但是由于只有一个泛型函数,所以该泛型函数的其他方法或属性也被共享,即使这是不需要的。这包括例如泛型函数的方法组合机制、:around
方法等。此外,如果任何库为标准类型(如数字的 :x-position
)定义了方法,则行为是共享的与其他图书馆。
这就是访问函数总是被定义为普通包符号的原因。
另一方面,:initarg
参数不会出现同样的问题。两个不相关的 class 具有相同的 :x-position
initarg 作为它们各自的插槽之一不是问题,因为每个关键字的使用上下文不同。
调用 make-instance
时,代码会分派到使用不同 initargs 的特定初始化代码。关键字 initarg 可能成为问题的唯一情况是 class 继承自两个依赖相同关键字的超级 classes,如以下示例所示:
USER> (defclass foo () ((x :initarg :x)))
#<STANDARD-CLASS FOO>
USER> (defclass bar () ((z :initarg :x)))
#<STANDARD-CLASS BAR>
USER> (defclass zot (foo bar) ())
#<STANDARD-CLASS ZOT>
USER> (make-instance 'zot :x 10)
#<ZOT {10252705D3}>
USER> (describe *)
#<ZOT {10252705D3}>
[standard-object]
Slots with :INSTANCE allocation:
Z = 10
X = 10
; No values
这里上面,同样的:x
initarg被用来初始化两个slot。幸运的是,如果这是一个很少见的问题,可以通过向插槽添加更多 initargs 来缓解:
USER> (defclass zot () ((x :initarg :foo-x) (z :initarg :bar-x)))
#<STANDARD-CLASS ZOT>
USER> (describe (make-instance 'zot :foo-x 5 :bar-x 10))
#<ZOT {10253A5373}>
[standard-object]
Slots with :INSTANCE allocation:
X = 5
Z = 10
我正在阅读 Sonja Keene 的书 Common Lisp 中的面向对象编程。
在第 9 章中,作者给出了以下示例:
(defclass window ()
((x :initarg :x-position :accessor x-position)
(y :initarg :y-position :accessor y-position)
(height :initarg :height :accessor window-height)
(width :initarg :width :accessor window-width)
(exposed-p :initform nil :accessor exposed-p))
(:documentation "Foundation of all windows"))
我知道在谈论 packages 时,:
、::
的使用或它的缺失超出了 Common Lisp 中的纯符号。 其使用背后有一个逻辑!
但是,我看不出在插槽 options/specifiers 上使用 :
背后的逻辑。例如,考虑上面的例子。
看第一个插槽,x
。存在与 :x-position
关联的插槽选项 :initarg
。好的,两者都包括 :
.
现在,在同一个插槽上,查看 :accessor
插槽选项。它包括双点 :
。但是,与之关联的 x-position
不 包括 :
.
这个语法背后有什么逻辑吗?
我最好的假设是语法设计是用来反映处理实例时的差异。因此,没有双点表示使用与 pos-initialization 相关,包含双点表示正在进行的初始化过程:
CL-USER> (x-position (make-instance 'window :x-position 3))
3
还有什么事吗?我错过了什么?是纯粹的语法约定,我说的只是巧合吗?
谢谢
:accessor
选项用于定义方法名。函数名称应始终位于应用程序的包中,这样您就不会在定义同名插槽的两个包之间发生冲突。
如果你使用关键字:x-position
,而其他一些包做同样的事情,它们会相互冲突。
记住,单个冒号表示符号 :bar
- 是关键字
- 并且在关键字包中
- 并从关键字包中导出
- 并且具有自身的常量值 (->
:bar
)。
尝试
(defclass foo ()
((x
:initarg x-position
:accessor x-position)))
* (make-instance 'foo 'x-position 3)
#<FOO {7007741DD3}>
* (x-position *)
3
和
(defclass foo ()
((x
:initarg x-position
:accessor :x-position)))
* (make-instance 'foo 'x-position 4)
#<FOO {700778D473}>
* (:x-position *)
4
初始化参数是函数make-instance
(及相关)的关键字参数。访问器是函数的名称和setf 访问器的名称.
可以使 initarg 成为任何符号,而不是关键字符号。它只是一个普通的关键字,因为函数关键字通常带有关键字符号。
也可以尝试使访问器名称成为一个符号,而不是具体的关键字符号。一个实现可能支持也可能不支持。这意味着实现需要支持关键字作为函数名称。
风格与惯例
通常我们把initarg选项写成关键字符号,accessor选项写成非关键字符号。
正如 Barmar 所解释的那样,使用关键字符号定义访问器函数将是冲突的根源。访问器函数是通用函数,因此在不同情况下重复使用相同的符号似乎仍然有效。例如,两个不同的不相关的 classes c1
或 c2
将 :x-position
定义为访问器可以如下使用,对于 [=36 的任何对象 o
=] c1
或 c2
:
(:x-position o)
唯一通用函数 :x-position
的动态调度会起作用。但是由于只有一个泛型函数,所以该泛型函数的其他方法或属性也被共享,即使这是不需要的。这包括例如泛型函数的方法组合机制、:around
方法等。此外,如果任何库为标准类型(如数字的 :x-position
)定义了方法,则行为是共享的与其他图书馆。
这就是访问函数总是被定义为普通包符号的原因。
另一方面,:initarg
参数不会出现同样的问题。两个不相关的 class 具有相同的 :x-position
initarg 作为它们各自的插槽之一不是问题,因为每个关键字的使用上下文不同。
调用 make-instance
时,代码会分派到使用不同 initargs 的特定初始化代码。关键字 initarg 可能成为问题的唯一情况是 class 继承自两个依赖相同关键字的超级 classes,如以下示例所示:
USER> (defclass foo () ((x :initarg :x)))
#<STANDARD-CLASS FOO>
USER> (defclass bar () ((z :initarg :x)))
#<STANDARD-CLASS BAR>
USER> (defclass zot (foo bar) ())
#<STANDARD-CLASS ZOT>
USER> (make-instance 'zot :x 10)
#<ZOT {10252705D3}>
USER> (describe *)
#<ZOT {10252705D3}>
[standard-object]
Slots with :INSTANCE allocation:
Z = 10
X = 10
; No values
这里上面,同样的:x
initarg被用来初始化两个slot。幸运的是,如果这是一个很少见的问题,可以通过向插槽添加更多 initargs 来缓解:
USER> (defclass zot () ((x :initarg :foo-x) (z :initarg :bar-x)))
#<STANDARD-CLASS ZOT>
USER> (describe (make-instance 'zot :foo-x 5 :bar-x 10))
#<ZOT {10253A5373}>
[standard-object]
Slots with :INSTANCE allocation:
X = 5
Z = 10