如何使用标准函数调用方法对象
How to call a method object with standard functions
如何将方法对象作为函数来调用?
Closer-mop 和 clos 包都提供了 method-function 来转一个方法对象转化为函数。但是,有没有办法在不包含另一个包的情况下做到这一点?如果不是,哪个包? (使用SBCL),但是如果需要一个包那么判别函数是怎么做的呢?
下面是一个使用find-method获取方法对象的例子。那么问题就是如何调用method-to-be-called.
(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
作为次要问题,文档说辨别函数首先尝试 compute-applicable-methods-by-class 来找到方法对象,然后如果失败,它会使用 compute-applicable-methods。为什么采用这种两层方法?假设 find-method 正在执行这种两层方法是否正确,所以最好使用 find-method ?
-- 附录 --
在下面的评论中,Rainer Joswig 指出这种查找方法形式依赖于实现:
(find-method #'inc '() '(a))) ; works on sbcl 1.3.1
他说说明符列表应该是 classes 并建议改为:
(find-method #'inc '() (list (find-class 'a))))
所以我想把我的 class 放在那里:
(find-method #'inc '() (list a)) ; crashes and burns
显然 (defclass a ... ) 不会将 a 设置为 class。事实上它没有设置任何东西!
* (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}>
*
所以 class 是来自 defclass 的 return 值,而不是提供给 defclass.[=16= 的符号的值]
对于 method-function
的特殊情况,SBCL 的 closer-mop 只需重新导出 sb-pcl
中的现有符号,如 [=22= 中所示]).
这意味着如果您正在使用 SBCL,您可以调用 sb-pcl:method-function
(PCL 表示可移植公共循环)。
泛型函数 compute-applicable-methods-by-class
让您知道哪些方法适用于给定的 类。如果您没有可以操作的实际实例,这将很有用。
似乎 compute-applicable-methods-using-classes
允许实现在第二个 return 值为 true
时记住适用的方法。此通用方法不允许您找到适用于 eql
专家的适用方法。
我在这里推测,但返回 compute-applicable-methods
以允许例如 eql
-specializers 或因为为 compute-applicable-methods
定义方法稍微容易一些是有意义的.
注意关于一致性的paragraph:
The following consistency relationship between compute-applicable-methods-using-classes and compute-applicable-methods must be maintained: for any given generic function and set of arguments, if compute-applicable-methods-using-classes returns a second value of true, the first value must be equal to the value that would be returned by a corresponding call to compute-applicable-methods. The results are undefined if a portable method on either of these generic functions causes this consistency to be violated.
我不认为在任何地方都指定了 find-method-using-classes
通用函数。
(find-method #'inc '() '(a))
以上无效。我们需要 类 的列表,而不是符号列表。
(funcall (method-function (find-method #'inc
'()
(list (find-class 'a))))
r)
由于函数 method-function
属于 MOP,许多实现都提供了它,并且它在一些实现特定的包中。 CLOSER-MOP
也使其可用。
但通常情况下,如果您已经在尝试提取方法函数,那么您可能以错误的方式使用 CLOS,或者您真的知道自己在做什么...
How does one call a method object as a function?
诚实的问题:你为什么要这样做?您是否首先指定了该方法的功能是如何构建的?
即使使用 closer-mop,我相信由 closer-mop:method-function
编辑的函数 return 至多在其 lambda 列表方面与 closer-mop:make-method-lambda
一致,所以也许你可以使用一个包来知道你可以移植什么。
方法的函数不必是与泛型函数具有相同 lambda 列表的函数,而且通常不是因为 next-method-p
和 call-next-method
。某些实现可能对下一个方法列表使用动态绑定,因此这些实现可能具有与通用函数一致的方法 lambda 列表。一般而言,不要指望它。
我相信 SBCL 不是这些实现之一,下一个方法列表传递给方法的函数以支持 next-method-p
和 call-next-method
。
Why do this two layer approach?
因为它允许在可能的情况下基于 classes 列表进行记忆(或缓存)。如果使用相同的classes的参数再次调用泛型函数,并且泛型函数没有被更新(参见MOP中的"Dependent Maintenance Protocol"),它可以重用最后的结果而不需要进一步处理,因为例如,通过将结果保存在哈希 table 中,其中键是 classes.
的列表
但是,如果 compute-applicable-methods-using-classes
return 是一个错误的第二个值,则使用 compute-applicable-methods
。原因是找不到单独使用 classes 的方法,这意味着某些方法具有非 class 专用程序。
这不同于说没有适用的方法,例如,如果所有方法都专门针对 classes 并且没有适用的方法,compute-applicable-methods-using-classes
应该 return空列表和真正的第二个值。调用 compute-applicable-methods
没有意义,它不会(或者更确切地说,如果实施得当,它不应该)进一步找到任何东西。
当使用 compute-applicable-methods
时仍然可以执行记忆,但记忆不再像使用 classes 列表作为散列中的键那样简单table。也许您可以使用树结构,在其中尝试为每个参数(实例,然后是 class)依次查找方法,直到树节点与整个可特殊化参数列表相匹配。
使用非标准专业化程序,您必须更改每个节点的搜索顺序。除非此类专家的优先级严格来说不在 eql
和 class 之前、之间或之后,否则您将进入未知领域。
实际上,您必须更改 compute-applicable-methods-using-classes
以识别非标准特化程序和 return false 及早,并且您必须更改 compute-applicable-methods
以处理这些特化程序, 无论如何,所以也许你会很好地了解如何记住 compute-applicable-methods
的结果。
Is it correct to assume the find-method is doing this two layer approach, so it is better to use find-method ?
不,find-method
的目的是寻找特定方法,而不是适用方法。它根本不使用 compute-applicable-methods-using-classes
或 compute-applicable-methods
。事实上,它永远不能使用后者,因为它需要实际参数而不是特化参数。
如何将方法对象作为函数来调用?
Closer-mop 和 clos 包都提供了 method-function 来转一个方法对象转化为函数。但是,有没有办法在不包含另一个包的情况下做到这一点?如果不是,哪个包? (使用SBCL),但是如果需要一个包那么判别函数是怎么做的呢?
下面是一个使用find-method获取方法对象的例子。那么问题就是如何调用method-to-be-called.
(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
作为次要问题,文档说辨别函数首先尝试 compute-applicable-methods-by-class 来找到方法对象,然后如果失败,它会使用 compute-applicable-methods。为什么采用这种两层方法?假设 find-method 正在执行这种两层方法是否正确,所以最好使用 find-method ?
-- 附录 -- 在下面的评论中,Rainer Joswig 指出这种查找方法形式依赖于实现:
(find-method #'inc '() '(a))) ; works on sbcl 1.3.1
他说说明符列表应该是 classes 并建议改为:
(find-method #'inc '() (list (find-class 'a))))
所以我想把我的 class 放在那里:
(find-method #'inc '() (list a)) ; crashes and burns
显然 (defclass a ... ) 不会将 a 设置为 class。事实上它没有设置任何东西!
* (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}>
*
所以 class 是来自 defclass 的 return 值,而不是提供给 defclass.[=16= 的符号的值]
对于 method-function
的特殊情况,SBCL 的 closer-mop 只需重新导出 sb-pcl
中的现有符号,如 [=22= 中所示]).
这意味着如果您正在使用 SBCL,您可以调用 sb-pcl:method-function
(PCL 表示可移植公共循环)。
泛型函数 compute-applicable-methods-by-class
让您知道哪些方法适用于给定的 类。如果您没有可以操作的实际实例,这将很有用。
似乎 compute-applicable-methods-using-classes
允许实现在第二个 return 值为 true
时记住适用的方法。此通用方法不允许您找到适用于 eql
专家的适用方法。
我在这里推测,但返回 compute-applicable-methods
以允许例如 eql
-specializers 或因为为 compute-applicable-methods
定义方法稍微容易一些是有意义的.
注意关于一致性的paragraph:
The following consistency relationship between compute-applicable-methods-using-classes and compute-applicable-methods must be maintained: for any given generic function and set of arguments, if compute-applicable-methods-using-classes returns a second value of true, the first value must be equal to the value that would be returned by a corresponding call to compute-applicable-methods. The results are undefined if a portable method on either of these generic functions causes this consistency to be violated.
我不认为在任何地方都指定了 find-method-using-classes
通用函数。
(find-method #'inc '() '(a))
以上无效。我们需要 类 的列表,而不是符号列表。
(funcall (method-function (find-method #'inc
'()
(list (find-class 'a))))
r)
由于函数 method-function
属于 MOP,许多实现都提供了它,并且它在一些实现特定的包中。 CLOSER-MOP
也使其可用。
但通常情况下,如果您已经在尝试提取方法函数,那么您可能以错误的方式使用 CLOS,或者您真的知道自己在做什么...
How does one call a method object as a function?
诚实的问题:你为什么要这样做?您是否首先指定了该方法的功能是如何构建的?
即使使用 closer-mop,我相信由 closer-mop:method-function
编辑的函数 return 至多在其 lambda 列表方面与 closer-mop:make-method-lambda
一致,所以也许你可以使用一个包来知道你可以移植什么。
方法的函数不必是与泛型函数具有相同 lambda 列表的函数,而且通常不是因为 next-method-p
和 call-next-method
。某些实现可能对下一个方法列表使用动态绑定,因此这些实现可能具有与通用函数一致的方法 lambda 列表。一般而言,不要指望它。
我相信 SBCL 不是这些实现之一,下一个方法列表传递给方法的函数以支持 next-method-p
和 call-next-method
。
Why do this two layer approach?
因为它允许在可能的情况下基于 classes 列表进行记忆(或缓存)。如果使用相同的classes的参数再次调用泛型函数,并且泛型函数没有被更新(参见MOP中的"Dependent Maintenance Protocol"),它可以重用最后的结果而不需要进一步处理,因为例如,通过将结果保存在哈希 table 中,其中键是 classes.
的列表但是,如果 compute-applicable-methods-using-classes
return 是一个错误的第二个值,则使用 compute-applicable-methods
。原因是找不到单独使用 classes 的方法,这意味着某些方法具有非 class 专用程序。
这不同于说没有适用的方法,例如,如果所有方法都专门针对 classes 并且没有适用的方法,compute-applicable-methods-using-classes
应该 return空列表和真正的第二个值。调用 compute-applicable-methods
没有意义,它不会(或者更确切地说,如果实施得当,它不应该)进一步找到任何东西。
当使用 compute-applicable-methods
时仍然可以执行记忆,但记忆不再像使用 classes 列表作为散列中的键那样简单table。也许您可以使用树结构,在其中尝试为每个参数(实例,然后是 class)依次查找方法,直到树节点与整个可特殊化参数列表相匹配。
使用非标准专业化程序,您必须更改每个节点的搜索顺序。除非此类专家的优先级严格来说不在 eql
和 class 之前、之间或之后,否则您将进入未知领域。
实际上,您必须更改 compute-applicable-methods-using-classes
以识别非标准特化程序和 return false 及早,并且您必须更改 compute-applicable-methods
以处理这些特化程序, 无论如何,所以也许你会很好地了解如何记住 compute-applicable-methods
的结果。
Is it correct to assume the find-method is doing this two layer approach, so it is better to use find-method ?
不,find-method
的目的是寻找特定方法,而不是适用方法。它根本不使用 compute-applicable-methods-using-classes
或 compute-applicable-methods
。事实上,它永远不能使用后者,因为它需要实际参数而不是特化参数。