【问题标题】:Request input regarding code style / best practices请求有关代码风格/最佳实践的输入
【发布时间】:2020-06-04 00:52:08
【问题描述】:

我正在尝试按照 Java (https://craftinginterpreters.com/control-flow.html) 中的这种实现来在 Common Lisp 中实现一种编程语言。

真正令人烦恼的一件事是到处都有大量的槽值。

当然,我可以使用 with-slots 在每个函数中添加一行,但这也是重复的,并且考虑到我的符号不是 :used 但我通过包引用它们,因为许多模块和我不想忘记它们的来源,即使 with-slots 也需要完整的限定名称。在我看来,这一切都像是非常糟糕的代码气味。

我用谷歌搜索并找到了 rutils。使用它的@object.slot 访问器,我设法极大地清理了我的代码。只看最后一次提交 https://github.com/AlbertoEAF/cl-lox/commit/84c0d62bf29bff9a2e0e77042a9108c168542b96?diff=split 摘录:

(我可以粘贴代码,但所有代码都可以在那个 repo 上找到,我想显示差异亮点)

它不仅删除了一些字符,而且更重要的是,需要考虑的深度(槽值调用)和括号的级别减少了 - 这对琐碎的函数有很大帮助。

当我有很多很多符号名称并且由于开始出现符号冲突而无法再将它们全部导出时,情况会变得更糟。

你们能给我关于代码的输入吗?现在看起来好多了,但我很好奇是否有更好的方法来解决它?

谢谢!

【问题讨论】:

  • Code Review 是提出编码风格问题的合适地点。
  • 您应该将代码发布为文本,而不是图像。图片难以阅读(您必须将它们展开到另一个窗口),不适合屏幕阅读器,并且无法剪切和粘贴到答案中。
  • 经验丰富的 CLOS 程序员只是习惯于使用slot-value 和/或with-slots
  • 我几乎从不使用槽值,而是定义了一个:reader 函数。如果插槽是可变的,则取决于预期谁来改变它:用户?然后我定义了一个:accessor 函数。同一包中的内部代码?具有未导出名称的 :writer 函数。访问器/读取器/写入器更具可读性,并且可以具有 :around 方法等来帮助强制执行属性/行为,这与 slot-value 不同。
  • 如果插槽是类的公共接口的一部分,则应该有一个访问器(访问器可能有也可能没有写入器,并且可能会或没有实际读取插槽:那不是该类的任何用户的业务)。任何使用slot-value 的东西都知道类实现的细节,因此是类实现的一部分。

标签: coding-style common-lisp clos


【解决方案1】:

在类定义中定义访问器函数还可以节省一些输入,而不必使用非标准语法

如果你像这样定义一个 CLOS 类:

(defclass person ()
  ((name
    :initarg :name)))

(defparameter *p* (make-instance 'person 
                     :name "John"))

*p*中访问name槽的槽值的唯一方法是:

(slot-value *p* 'name)
;; "John"

(with-slots (name) *p*
  name)
;; "John"

(with-slots ((nm name)) *p*
  nm)
;; "John"

但是如果你为每个槽定义一个:accessor,你可以使用函数名 作为:accessor 的参数给出,不必使用slot-valuewith-slots,以 访问并对其进行变异 (setf-able!)。

(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.
;; 

【讨论】:

    猜你喜欢
    • 2018-05-17
    • 2012-02-24
    • 1970-01-01
    • 2021-05-07
    • 1970-01-01
    • 2020-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多