lisp:如何在范围内创建临时方法专门化

lisp:如何在范围内创建临时方法专门化,lisp,common-lisp,clos,mop,Lisp,Common Lisp,Clos,Mop,在OP中,他要求类似的东西。但是我想创建一个方法专门化器,而不是一个函数。 基本上假设方法定义如下: defmethod my-meth ((objA classA) (objB classB)) (...) 我想做的是(伪代码): 真正的用途是我想创建一个临时环境,setf slot value using class将专门用于eql,本质上创建一个特定对象对其slot写入的按需截取。(目的是在某处记录新旧插槽值,然后调用next方法。)我不想创建元类,因为我可能想截取已经实例化的标准对象

在OP中,他要求类似的东西。但是我想创建一个方法专门化器,而不是一个函数。 基本上假设方法定义如下:

defmethod my-meth ((objA classA) (objB classB)) (...)
我想做的是(伪代码):

真正的用途是我想创建一个临时环境,
setf slot value using class
将专门用于
eql
,本质上创建一个特定对象对其slot写入的按需截取。(目的是在某处记录新旧插槽值,然后调用next方法。)我不想创建元类,因为我可能想截取已经实例化的标准对象

当然,我尝试过,但没有成功(因为如何在
标签
中使用
DEFMETHOD
?),但我想让一些更有经验的人来验证这种方法是否可行,并/或提出一种合适的方法

评论

编辑:

Daniel和Terje提供了极好的链接,可以拓宽我对各种可能性的了解,但在去那里之前,我想在寻找更普通的方法方面多做一点努力。我一直在研究在进入环境时执行add方法,该方法将专门用于eql,并在退出时执行remove方法。我还没做完。如果有人玩过这些,评论会很好。将使线程保持最新

编辑2:我即将使用添加方法场景完成此操作,但存在一个问题。以下是我尝试过的:

    (defun inject-slot-write-interceptor (object fun)
    (let* ((gf (fdefinition '(setf sb-mop:slot-value-using-class)))
            (mc (sb-mop:generic-function-method-class gf))
            (mc-instance (make-instance (class-name mc) 
                            :qualifiers '(:after)
                            :specializers (list (find-class 't)
                                                (find-class 'SB-PCL::STD-CLASS)
                                                (sb-mop::intern-eql-specializer object) 
                                                (find-class 'SB-MOP:STANDARD-EFFECTIVE-SLOT-DEFINITION))
                           :lambda-list '(new-value class object slot)
                           :function (compile nil (lambda (new-value class object slot) (funcall fun new-value class object slot))))))
         (add-method gf mc-instance)
         (defun remove-slot-write-interceptor ()
            (remove-method gf mc-instance))
       ))

(defun my-test (object slot-name data)
     (let ((test-data "No results yet") 
           (gf (fdefinition '(setf sb-mop::slot-value-using-class))))
       (labels ((show-applicable-methods () (format t "~%Applicable methods: ~a" (length (sb-mop:compute-applicable-methods gf (list data (class-of object) object (slot-def-from-name (class-of object) slot-name)))))))
            (format t "~%Starting test: ~a" test-data)
            (show-applicable-methods)
            (format t "~%Injecting interceptor.")
            (inject-slot-write-interceptor object (compile nil (lambda (a b c d) (setf test-data "SUCCESS !!!!!!!"))))
            (show-applicable-methods)
            (format t "~%About to write slot.")
            (setf (slot-value object slot-name) data)
            (format t "~%Wrote slot: ~a" test-data)
            (remove-slot-write-interceptor)
            (format t "~%Removed interceptor.")
            (show-applicable-methods)
       )))      
使用某些对象插槽和数据作为args调用(我的测试)会导致:

Starting test: No results yet 
Applicable methods: 1 
Injecting interceptor. 
Applicable methods: 2 
About to write slot.     
Wrote slot: No results yet  <----- Expecting SUCCESS here....
Removed interceptor. 
Applicable methods: 1
开始测试:还没有结果
适用方法:1
注入式拦截器。
适用方法:2
即将写入插槽。

写入插槽:还没有结果不,您不能在Common Lisp中定义动态范围或词汇范围的专用方法。

可以用作解决潜在问题的方法。另见

是一个为公共Lisp/CLOS提供面向方面/上下文扩展的库

一种轻量级的替代方法是使用一个特殊/动态变量来指示该方法何时应该进行日志记录:

(defparameter *logging* NIL "Bind to a true value to activate logging")

(defmethod my-meth :around ((objA classA) (objB (eql some-object)))
  (prog2 
   (when *logging*
     (logging "Enter my-meth"))
   (call-next-method)
   (when *logging*
     (logging "Exit my-meth"))))

(let ((*logging* T))
   (do stuff calling my-meth with the object...))

但是请注意:around方法在禁用日志记录时也会被调用。

似乎曾经有
通用flet
通用标签
为Common Lisp提出过特殊形式,但它被从最终规范中删除,因为它被认为很少得到实现的支持,而且设计也很差。参见HyperSpec中的。阅读关于为什么人们认为词汇方法不如词汇范围函数有用的讨论很有趣


因此,如果没有这些,我真的不认为有办法创建词汇范围的方法,尽管我还没有在另一个答案中使用Terje链接到的ContextL,所以它可能会提供您需要的内容。

至少对我来说,原因真的不存在。这一次,将泛型函数本地化的有用性与类定义本地化的有用性绑定在一起对我来说毫无意义。似乎他们只是想要一个根本不存在的标准。对我来说,这是移除它们的唯一合理理由。但是,我需要这个,或者更真诚地说,我会喜欢它:“我认为我的用例是一个很好的例子。如果添加/删除方法不起作用,我可能会这样做,但是我希望有一个完全动态的方式……考虑使用宏来构造语法糖,这样你就可以用你想做的方式来编码“方法重新定义”,即使底层实现是一个较小的hack。方法定义是全局的,因此在词汇或动态环境中添加和删除方法将影响其他线程(假设是多处理)。嗯,狗屎。。。除非。。。在我的用例中,我想专门处理一个特定的实例,因此如果该实例是共享的,那么所有线程都会受到影响,这正是我想要的,如果它不是共享的,那么动态方法将不适用。虽然我希望像一个更抽象的“工具式”包装器。。。感谢铃声。为了全局性地启用一个特定实例的日志记录,请考虑使用<代码>更改类< /代码>将实例的类更改为具有<代码>我的METH的子类:执行日志记录的< < /Cord>方法,然后通过更改回原来的类来禁用日志记录。使用宏来编写抽象包装。你有很多想法:)谢谢:)我不接受泰耶的答案,原因有二:1.我意识到这会误导未来的任何人。我想接受的是他关于变革阶级的评论。2.我开始更加坚定地相信add方法场景,因为(如果它最终能够工作的话)它可以是一个简单的宏,而不需要任何额外的元类麻烦,尽管无可否认这是一个小麻烦。
(defparameter *logging* NIL "Bind to a true value to activate logging")

(defmethod my-meth :around ((objA classA) (objB (eql some-object)))
  (prog2 
   (when *logging*
     (logging "Enter my-meth"))
   (call-next-method)
   (when *logging*
     (logging "Exit my-meth"))))

(let ((*logging* T))
   (do stuff calling my-meth with the object...))