如何使用标准函数调用方法对象

时间:2016-02-05 09:27:15

标签: methods common-lisp clos ansi-common-lisp

如何将方法对象作为函数调用?

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

以下是使用find-method获取方法对象的示例。问题是如何调用要调用的方法

(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的符号的值。

3 个答案:

答案 0 :(得分:4)

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

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

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

由于函数method-function属于MOP,因此许多实现提供了它,并且它在某些特定于实现的包中。 CLOSER-MOP也可以使用它。

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

答案 1 :(得分:3)

  

如何将方法对象作为函数调用?

诚实的问题:你为什么要那样做?您是否首先指定了方法的功能是如何构建的?

即使使用close-mop,我相信closer-mop:method-function返回的函数最多也与closer-mop:make-method-lambda的lambda列表一致,所以也许你可以用一个包来知道你可以依靠的东西。

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

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

  

为什么这两层接近?

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

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

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

当使用compute-applicable-methods时,仍然可以执行memoization,但是memoization不再像使用类列表作为哈希表中的键那样微不足道。也许您可以使用树结构,在这种树结构中,您尝试按顺序为每个参数查找每个特化器(实例,然后是类)的方法,直到树节点与整个特殊化参数列表匹配。

使用非标准专精,您必须更改每个节点的搜索顺序。除非这些专业人员的优先权不是严格地在eql之前,之间或之后,否则你就处于未知领域。

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

  

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

不,find-method的目的是找到特定的方法,而不是适用的方法。它根本不使用compute-applicable-methods-using-classescompute-applicable-methods。事实上,它永远不能使用后者,因为它需要实际的参数而不是特化器。

答案 2 :(得分:2)

对于method-function的特定情况, close-mop 对于SBCL,只需从sb-pcl重新导出现有符号,如closer-mop-packages.lisp中所示。整个文件使用读取时间条件(参见1.5.2.1 Use of Implementation-Defined Language Features)。 这意味着如果您正在使用SBCL,则可以调用sb-pcl:method-function(PCL表示便携式公共循环)。

泛型函数compute-applicable-methods-by-class允许您知道哪些方法适用于给定的类。如果您没有可以操作的实际实例,这将非常有用。 似乎compute-applicable-methods-using-classes允许实现在第二个返回值为true时记住适用的方法。此通用方法不允许您查找专用于eql特化器的适用方法。

我在这里推测,但有必要回到compute-applicable-methods以允许例如eql - 特化器,或者因为为compute-applicable-methods定义方法稍微容易一些。 请注意关于一致性的paragraph

  

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

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