1

如何将方法对象称为函数?

Closer-mopclos包都提供了将方法对象转换为函数的方法函数。但是,有没有办法在不包含另一个包的情况下做到这一点?如果没有,哪个包?(使用 SBCL),但是如果需要一个包,那么鉴别功能是如何做到的呢?

这是使用 find-method 获取方法对象的示例。那么问题是如何调用method-to-be- call 。

(defclass a () ((x :accessor x :initform 0)))
(defgeneric inc (i))
(defmethod inc ((i a)) (incf (x i)))
(defvar r (make-instance 'a))

;; ... in a land far far away:    
(defvar method-to-be-called (find-method #'inc '() '(a)))

(funcall method-to-be-called r);; crashes and burns

作为第二个问题,文档说鉴别功能首先尝试按类计算适用方法以查找方法对象,如果失败,则使用计算适用方法。为什么要采用这种两层方法?假设find-method正在执行这种两层方法是否正确,所以最好使用find-method

-- 附录 -- 在下面的评论中,Rainer Joswig 指出这种查找方法形式是依赖于实现的:

(find-method #'inc '() '(a))) ; works on sbcl 1.3.1

他说说明符列表应该是类并建议:

(find-method #'inc '() (list (find-class 'a))))

所以我想把我的课放在那里:

(find-method #'inc '() (list a))  ; crashes and burns

显然(defclass a ... )没有将a设置为类。事实上,它并没有设置任何东西!

* (defclass a () ((x :accessor x :initform 0)))
#<STANDARD-CLASS COMMON-LISP-USER::A>
* a

... 变量 A 未绑定。

但是,这有效:

* (defvar ca (defclass a () ((x :accessor x :initform 0))))
CA
* (defmethod inc ((i a)) (incf (x i)))
WARNING: Implicitly creating new generic function COMMON-LISP-USER::INC.
#<STANDARD-METHOD COMMON-LISP-USER::INC (A) {1005EE8263}>
enter code here
* (find-method #'inc '() (list ca))   
#<STANDARD-METHOD COMMON-LISP-USER::INC (A) {1005EE8263}>
* 

所以类是 defclass 的返回值,而不是提供给 defclass 的符号的值。

4

3 回答 3

4
(find-method #'inc '() '(a))

以上不起作用。我们需要一个类列表,而不是符号列表。

(funcall (method-function (find-method #'inc
                                       '()
                                       (list (find-class 'a))))
         r)

由于该功能method-function属于 MOP,因此许多实现都提供了它,并且它位于某些实现特定的包中。CLOSER-MOP也使它可用。

但通常,如果您已经在尝试提取方法函数,那么您可能以错误的方式使用 CLOS,或者您真的知道自己在做什么......

于 2016-02-05T10:25:48.350 回答
3

如何将方法对象称为函数?

诚实的问题:你为什么要这样做?您是否一开始就指定了方法的功能是如何构建的?

即使使用更紧密的拖把,我相信返回的函数closer-mop:method-function最多与closer-mop:make-method-lambda它的 lambda-list 一致,所以也许你可以使用一个包来知道你可以依靠什么便携。

方法的函数不必是与泛型函数具有相同 lambda-list 的函数,而且通常不是由于next-method-pand call-next-method。一些实现可能对下一个方法列表使用动态绑定,因此这些实现可能具有与泛型函数一致的方法 lambda-list。只是不要指望它,一般来说。

我相信 SBCL 不是这些实现之一,下一个方法列表被传递给方法的函数以支持next-method-pcall-next-method.

为什么要采用这种两层方法?

因为它允许在可能的情况下基于类列表进行记忆(或缓存)。如果使用相同类的参数再次调用泛型函数,并且泛型函数尚未更新(参见 MOP 中的“依赖维护协议”),它可以重用最后一个结果而无需进一步处理,例如,通过保持生成一个哈希表,其中键是类列表。

但是,如果compute-applicable-methods-using-classes返回错误的第二个值,则compute-applicable-methods使用 then。原因是无法单独使用类找到方法,这意味着某些方法具有非类专用器。

这与说没有适用的方法不同,例如,如果所有方法都专门用于类并且没有适用的方法,compute-applicable-methods-using-classes则应该返回一个空列表和一个真正的第二个值。调用 没有意义compute-applicable-methods,它不会(或者更确切地说,如果实施得当,它不应该)进一步找到任何东西。

使用时仍然可以执行记忆compute-applicable-methods,但记忆不再像使用类列表作为哈希表中的键那样简单。也许您可以使用树结构,在其中尝试为每个参数顺序查找每个专用程序(实例,然后是类)的方法,直到树节点与整个可专用参数列表匹配。

使用非标准专家,您必须更改每个节点的搜索顺序。除非此类专家的优先级不是严格地在eql课程之前、之间或之后,否则您将处于未知领域。

实际上,您必须更改compute-applicable-methods-using-classes以识别非标准的专家并尽早返回false compute-applicable-methods,无论如何您必须更改以处理这些专家,所以如果可能的话,您可能会很好地了解如何compute-applicable-methods无论如何都要记住结果。

假设 find-method 正在执行这种两层方法是否正确,所以最好使用 find-method ?

不,目的find-method是找到一个具体的方法,而不是一个适用的方法。它不使用compute-applicable-methods-using-classes或根本不使用compute-applicable-methods。事实上,它永远不能使用后者,因为它需要实际参数而不是专家。

于 2016-02-05T19:02:43.713 回答
2

对于SBCL 的 close -mop的特殊情况method-function,只需从 中重新导出现有符号,如close -mop-packages.lisp所示。整个文件使用读取时条件(请参阅1.5.2.1 使用实现定义的语言特性)。这意味着如果您正在使用 SBCL,您可能会调用(PCL 表示 Portable Common Loops)。sb-pclsb-pcl:method-function

泛型函数compute-applicable-methods-by-class允许您知道哪些方法适用于给定的类。如果您没有可以操作的实际实例,这很有用。当compute-applicable-methods-using-classes第二个返回值为true. 这种泛型方法不允许您找到专门用于eql专家的适用方法。

我在这里推测,但是回退到compute-applicable-methods允许例如eql-specializers 或者因为为compute-applicable-methods. 注意关于一致性的段落:

必须保持compute-applicable-methods-using-classes和compute-applicable-methods之间的以下一致性关系:对于任何给定的通用函数和参数集,如果compute-applicable-methods-using-classes返回第二个值true ,第一个值必须等于相应调用计算适用方法返回的值。如果这些通用函数中的任何一个上的可移植方法导致违反这种一致性,则结果是不确定的。

我认为在find-method-using-classes任何地方都没有指定通用函数。

于 2016-02-05T09:58:51.370 回答