Common Lisp 中的方法有命名约定吗?

Is there a naming convention for methods in Common Lisp?

我发现用 Common Lisp 进行面向对象编程很痛苦。主要问题是 methods.Please 的命名见下面的代码。

(defclass foo () ())

(defmethod action ((object foo))
  (write-line "hello"))


(defclass bar () ())

(defmethod action ((object bar) boo)
  t)


(print
 (action (make-instance 'foo)))

(print
 (action (make-instance 'bar) 1))

这两个 action 方法不是同一通用函数的不同实现。他们偶然有相同的名字。 但是 Common Lisp 要求所有具有相同名称的方法具有相同数量的参数。

为了避免名称冲突,我通常在方法名称前加上class名称,例如foo-actionbar-action。但这会导致代码在实际程序中非常冗长,例如(lengthy-class-name-do-something some-variable).

其他面向对象的编程语言,如C++和Java,没有这样的问题。您可以像 some_variable.do_something() 这样写,其中没有名称冲突。

所以我想知道对于上述问题是否有更好的解决方案?

关键区别确实是方法是泛型函数的方法。它们不是 类.

上的方法

这听起来像是为了文字而文字。但它应该会极大地影响您的命名选择。 “Action”是通用函数的一个非常平淡的名称。当 foo 起作用时,它实际上做了什么?当bar行动时,它实际上做了什么?

如果 action 确实是最好的名字,那么 foo:actionbar:action 是否有意义(即 [=10= 中的 action ] 包,bar 包中有一个)?他们是不同的GF。当然,缺点是它不再像对它们调用 action 那样简单。

这暗示“你不需要方法,你可以只使用一个函数”,因为你不需要需要一个方法来用类 在 CL.

这两个方法 相同通用函数的方法,因此它们绝对必须具有兼容的 lambda 列表。否则,无法保证有效方法组合的解析(发生在运行时,因为这是运行时多态性)具有明确的结果。

你省略了 defgeneric 形式,它应该会给你一个来自编译器的警告。

如您所述,这仅适用于 Java,因为您通常将每个 class 放入其自己的命名空间。但是在这里,您在同一个名称空间中工作,并期望相同的名称(符号)具有两种完全不同的含义。这行不通。

用Java的说法,每个泛型函数都是它自己的接口。

另外,为了说明优点:您不需要将此接口的实现附加到 class,但您甚至可以有多个 classes 来分派(在运行时) .这使得e。 G。众所周知的复杂访客模式已过时。

想象一下 Java 中顶层的这种语法(即作为 classinterface:

的替代方案
method IAction (Foo foo, Bar bar) {
  action (Foo foo, Bar bar) {
    return something(...);
  }
}

当在运行时调用 IAction#action 获取 class FooBar 的对象时,将调用此方法。当然,这在 Java 中不存在(而且似乎很难添加)。

这是您定义 class

的方式
(defclass foo () ())

问题是您没有用任何插槽定义 class。像这样:

(defclass foo ()
    ((action 
        :initarg :action
        :accessor action)))

这会添加 action,它是 class foo,

的访问器(getter 和 setter)

:initarg:action是初始化时用来给它赋值的字

(defvar my-foo-class (make-instance 'foo :action "bar" ))

然后我们可以访问值

(action my-foo-class)
output> "bar"

我建议看看这个 tutorial on CLOS

方法没有通用的命名约定,函数也没有命名约定。名称冲突的情况可能会根据上下文不同解决。

  1. 第一个问题是:如果只有两个方法具有不同的功能和冲突的名称,为什么不考虑普通函数(具有不同的名称)。如果您以非常通用的方式命名某个函数(普通的或通用的),则意味着该函数无处不在(这种情况很少见)。在这种情况下,这个函数的输入集应该或多或少是统一的(即使它对不同的参数类型有不同的方法),但是你可以使用 &key(和 &allow-other-keys)来处理随着附加参数的变化。然而,在大多数情况下,为两个语义上不同的动作赋予更多不同的名称将更有利于以后的代码维护。

  2. 如果两个或多个不同的动作仍想保留相同的名称,可以将相关代码放在不同的包中。对于具有不同功能的代码,这样做可能是非常合理的。现在,您将无法同时将这两个函数导入到其他包中,因为它仍然会导致名称冲突。但是您将能够使用包前缀名称:p1:foop2:foo(如果它们都将被导出)。包是解决不可避免的名称冲突的标准 Lisp 方法。在这种情况下,我们可以说在许多其他面向对象语言中 类 扮演着这个角色。