我正在尝试通过遵循Java(https://craftinginterpreters.com/control-flow.html)中的这种实现来在Common Lisp中实现一种编程语言。
真正令人烦恼的事情之一就是到处都有很多插槽值。
保证我可以在每个带有with-slot的函数中添加一行,但这也是重复的,并且鉴于我的符号不是:us,而是由包引用,因为许多模块中都有很多符号,我不想丢失它们的来源,即使是插槽也需要完整的合格名称。在我看来,所有这些似乎都非常难闻的代码气味。
我用Google搜索并找到了rutils。通过其@ object.slot访问器,我设法极大地清理了我的代码。只需查看最后的提交https://github.com/AlbertoEAF/cl-lox/commit/84c0d62bf29bff9a2e0e77042a9108c168542b96?diff=split摘录:
(我可以粘贴代码,但是所有代码都可以在该仓库中使用,我想显示差异突出显示)
不仅删除了一些字符,而且更重要的是,要考虑的深度(插槽值调用)和括号要少一个级别,这对平凡的功能很有帮助,但对平凡的功能来说是有用的。
[当我有很多符号名称时,情况变得更糟,因为我开始在符号中出现冲突,因此无法再全部导出它们。
你们能给我输入代码吗?现在看起来好多了,但我很好奇,似乎是更好的解决方法?
谢谢!
如果您这样定义CLOS类:
(defclass person ()
((name
:initarg :name)))
(defparameter *p* (make-instance 'person
:name "John"))
访问name
中*p*
插槽的插槽值的唯一方法是:
(slot-value *p* 'name)
;; "John"
(with-slots (name) *p*
name)
;; "John"
(with-slots ((nm name)) *p*
nm)
;; "John"
但是如果为每个插槽定义一个:accessor
,则可以使用功能名称作为:accessor
的参数给出,而不必使用slot-value
或with-slots
访问并对其进行更改(setf
-可行!)。
(defclass person ()
((name
:initarg :name
:accessor nm)))
(nm *p*)
;; "John"
(setf (nm *p*) "Doe")
;; "Doe"
(nm *p*)
;; "Doe"
然而,惯例是,也将插槽名称用作:accessor
方法的名称:
(defclass person ()
((name
:initarg :name
:accessor name))) ;; but better use the slot name as accessor
(name *p*)
;; "John"
(setf (name *p*) "Doe")
(name *p*)
;; "Doe"
:accessor
方法(通用函数)特定于以下对象这个班。因此,您不必担心名称空间冲突。
(defclass house ()
((address
:initarg :address
:accessor addr)))
;; you cannot use `address` because it is already occupied
;; by a system's function/symbol -> see under `(describe 'address)`
(defparameter *h* (make-instance 'house :address "Bakerstreet 1"))
(name *h*)
;; EVAL: undefined function name OR:
;; NO-APPLICABLE-METHOD error (in the case
;; that other classes exist with a `name` accessor method.
(addr *h*)
;; "Bakerstreet 1"
(addr *p*)
;; *** - NO-APPLICABLE-METHOD: When calling #<STANDARD-GENERIC-FUNCTION ADDR>
;; with arguments (#<PERSON #x1B06B79E>), no method is applicable.
;;