如何将方法对象作为函数调用?
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的符号的值。
答案 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-p
和call-next-method
。某些实现可能会对下一个方法列表使用动态绑定,因此这些方法可能具有与泛型函数一致的方法lambda-list。一般不要指望它。
我认为SBCL不是这些实现之一,下一个方法列表被传递给方法的函数以支持next-method-p
和call-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-classes
或compute-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
通用函数。