Common lisp 如何从外部包重载方法

Common lisp 如何从外部包重载方法,common-lisp,Common Lisp,考虑到szenario,您得到了一个基本包,它可以通过一个类表示某些内容,另一个包希望扩展此功能 (defpackage :test (:use :cl) (:nicknames :test) (:export a-test-class method-a slot-a)) (in-package :test) (defclass a-test-class () ((slot-a :initform 42 :reader slot-a

考虑到szenario,您得到了一个基本包,它可以通过一个类表示某些内容,另一个包希望扩展此功能

(defpackage :test
  (:use :cl)
  (:nicknames :test)
  (:export a-test-class
       method-a
       slot-a))

(in-package :test)

(defclass a-test-class ()
  ((slot-a
    :initform 42
    :reader slot-a)))


(defmethod method-a ((a-test-class a-test-class))
  (setf (slot-value a-test-class 'slot-a) 21)
  a-test-class)


(defpackage :exttest
  (:use :cl)
  (:export extended-a-test-class
       method-a))

(in-package :exttest)

(defclass extended-a-test-class (test:a-test-class)
  ((slot-b 
    :reader slot-b
    :initform nil)))

(defmethod method-a ((a-test-class extended-a-test-class))
  (setf (slot-value a-test-class 'slot-a) 23)
  a-test-class)
现在我得到了一个函数,它实际上不做anthying,而是遍历
a-test-class
extended-a-test-class
的实例列表,并且应该对所有这些类调用
method-a
,期望它们分别更改为它们的类型。例如
(slot-a(方法a-test-class-instance))>21
(slot-a(方法a-extended-a-test-class-instance))>23

但为了做到这一点,我遇到了正确调用方法a的问题:

(defparameter *test-instance* (make-instance 'test:a-test-class))
(defparameter *ext-test-instance* (make-instance 'exttest:extended-a-test-class))

(test:slot-a (test:method-a *test-instance*))
> 21
(test:slot-a (test:method-a *ext-test-instance*))
> 21

(测试:slot-a(exttest:method-a*测试实例*)
(测试:slot-a(exttest:method-a*ext-test实例*)
在线程中的简单错误上调用调试器
#:
没有适用于泛型函数的方法
#
当使用参数调用时
(#)
这两种方法都不适用于我,因为我无法编译,或者方法的效果不理想。如果类和方法定义在同一个包中,那么一切都可以正常工作

因此:我如何在实例上调用方法而不需要处理相应的包?(如果我不能这样做,我想知道我对Common Lisp中OO编程的期望是如何被误导的)

对于一个“工作”示例,输出输出的内容,我编写了C++程序。我知道CLOS的工作原理不同于“通用”面向对象系统,因为方法不“属于”类。但我希望任何面向对象的系统(以某种方式)都能像这样运行/使用:

#include <iostream>

namespace test {
  class sub {
  public:
    virtual sub* method_a() = 0;
  };

  class a_test_class : public sub
  {
  protected:
    int value;  
  public:
   a_test_class(int val) : value(val) {
   }

   a_test_class* method_a() {
     value = 21;
     return this;
   }

   int get_value() {
      return value;
   }
  };
}

namespace exttest {

  class extended_a_test_class : public test::a_test_class {

  public:
    extended_a_test_class(int val) : a_test_class(val) {  }

   extended_a_test_class* method_a() {
     std::cout << "calling overloaded method" << std::endl;
     this->value = 23;
     return this;
   }
  };
}


int main(int argc,const char* argv[]) {
  test::a_test_class* atc = new test::a_test_class(42);
  test::a_test_class* eatc = new exttest::extended_a_test_class(42);
  std::cout << atc->method_a()->get_value() << std::endl;
  std::cout << eatc->method_a()->get_value() << std::endl;
  delete atc;
  delete eatc;
}

> ./a.out
21
calling overloaded method
23
#包括
名称空间测试{
班子{
公众:
虚子*方法_a()=0;
};
a级\测试\等级:公共子系统
{
受保护的:
int值;
公众:
测试类(int-val):值(val){
}
a_test_class*方法_a(){
数值=21;
归还这个;
}
int get_值(){
返回值;
}
};
}
命名空间exttest{
类扩展测试类:公共测试::测试类{
公众:
扩展的测试类(int-val):测试类(val){}
扩展的_a_测试_类*方法_a(){
std::cout get_value()get_value()./a.out
21
调用重载方法
23
您需要将
(:从测试导入:方法-a)
添加到
(defpackage:exttest)
,这样两个符号
测试:方法-a
exttest:方法-a
将是相同的

按照现在的定义,有两个单独的通用函数
exttest:method-a
test:method-a
,每个函数都有一个方法;前者没有为
exttest:extended-a-test-class
定义,而后者没有为
exttest:extended-a-test-class

定义单独的方法,您需要添加
ode>(:从测试导入:方法-a)
(defpackage:exttest)
,这样两个符号
test:method-a
exttest:method-a
将是相同的


正如现在定义的那样,有两个单独的通用函数
exttest:method-a
test:method-a
,每个函数都有一个方法;前者没有为
exttest:extended-a-test-class
定义,而后者没有像您可能看到的那样为
exttest:extended-a-test-class

定义单独的方法ED编写一个包前缀,以确保在调用函数或方法时,您指的是正确的符号,因此,当您定义泛型函数上的附加方法时,可能需要使用包前缀。例如,考虑一个名为“FO”的包,以及一个名为“FROB”的包中的泛型函数“FoO”:

在另一个名为“ΒΑR”的包中,为了引用名为“FOO”的包中名为“FROB”的符号,我们必须编写
FOO:FROB
。这用于调用泛型function,以及在其上定义新方法

(defpackage #:bar
  (:use "COMMON-LISP"))

(defmethod foo:frob ((x integer))
  (format t "frobbing integer ~A" x))

;; call frob 
(foo:frob "some string")
(foo:frob 45)

正如您可能需要编写一个包前缀,以确保在调用函数或方法时引用的是正确的符号,因此,当您定义泛型函数上的附加方法时,可能需要使用包前缀。例如,考虑一个名为“FO”的包,以及一个名为“FROB”的包中的泛型函数“FO”。:

在另一个名为“ΒΑR”的包中,为了引用名为“FOO”的包中名为“FROB”的符号,我们必须编写
FOO:FROB
。这用于调用泛型function,以及在其上定义新方法

(defpackage #:bar
  (:use "COMMON-LISP"))

(defmethod foo:frob ((x integer))
  (format t "frobbing integer ~A" x))

;; call frob 
(foo:frob "some string")
(foo:frob 45)

方法可能不属于类,但它们确实属于泛型函数。我发现定义泛型函数(使用
defgeneric
)通常很有帮助在为它们定义方法之前。当您使用
defmethod
隐式创建泛型函数时,至少SBCL也会发出警告。这可能对您有所帮助。@Svante我确实看到有新的泛型方法被定义,但我不知道如何防止。但仍然是一个很有价值的建议,谢谢。方法可能不属于它o类,但它们确实属于泛型函数。我发现定义泛型函数(使用
defgeneric
)通常很有帮助在为它们定义方法之前。当您使用
defmethod
隐式创建泛型函数时,至少SBCL还会发出警告。这可能对您有所帮助。@Svante我确实看到有新的泛型方法被定义,但我不知道如何防止。但仍然是一个很有价值的建议,谢谢。
(defpackage #:bar
  (:use "COMMON-LISP"))

(defmethod foo:frob ((x integer))
  (format t "frobbing integer ~A" x))

;; call frob 
(foo:frob "some string")
(foo:frob 45)