如何在Clojure中使用钩子
我在一个clojure命名空间中创建和销毁对象,并希望另一个命名空间进行协调。但是,我不希望第一个命名空间必须在对象销毁时显式调用第二个命名空间 在Java中,我可以使用侦听器。不幸的是,底层java库不会发出对象破坏事件的信号。如果我在Emacs Lisp中,那么我会使用钩子来完成这个任务 现在,在clojure,我不太确定。我找到了罗伯特·胡克图书馆。但这更像是elisp术语中的defadvice——我正在编写函数。更多文档说明: “钩子是用来扩展你无法控制的函数;如果你拥有目标函数,显然有更好的方法来改变它的行为。” 可悲的是,我并不觉得这很明显 另一种可能是使用addwatch,但它被标记为alpha 我是否错过了另一个显而易见的解决方案 添加的示例: 所以第一个名称空间如何在Clojure中使用钩子,clojure,hook,Clojure,Hook,我在一个clojure命名空间中创建和销毁对象,并希望另一个命名空间进行协调。但是,我不希望第一个命名空间必须在对象销毁时显式调用第二个命名空间 在Java中,我可以使用侦听器。不幸的是,底层java库不会发出对象破坏事件的信号。如果我在Emacs Lisp中,那么我会使用钩子来完成这个任务 现在,在clojure,我不太确定。我找到了罗伯特·胡克图书馆。但这更像是elisp术语中的defadvice——我正在编写函数。更多文档说明: “钩子是用来扩展你无法控制的函数;如果你拥有目标函数,显然有
(ns scratch-clj.first
(:require [scratch-clj.another]))
(def listf (ref ()))
(defn add-object []
(dosync
(ref-set listf (conj
@listf (Object.))))
(println listf))
(defn remove-object []
(scratch-clj.another/do-something-useful (first @listf))
(dosync
(ref-set listf (rest @listf)))
(println listf))
(add-object)
(remove-object)
第二命名空间
(ns scratch-clj.another)
(defn do-something-useful [object]
(println "object removed is:" object))
这里的问题是scratch-clj.first需要另一个并显式地推送删除事件。这有点笨重,但如果我有“另一个”名称空间,也不起作用,它也想听
因此,我考虑挂接第一个函数。听起来很像您描述的是回调 比如:
(defn make-object
[destructor-fn]
{:destructor destructor-fn :other-data "data"})
(defn destroy-object
[obj]
((:destructor obj) obj))
; somewhere at the calling code...
user> (defn my-callback [o] (pr [:destroying o]))
#'user/my-callback
user> (destroy-object (make-object my-callback))
[:destroying {:destructor #<user$my_callback user$my_callback@73b8cdd5>, :other-data "data"}]
nil
user>
(定义生成对象)
[析构函数fn]
{:析构函数析构函数fn:其他数据“数据”})
(defn)销毁对象
[obj]
((:析构函数obj)obj))
; 在呼叫代码的某个地方。。。
用户>(定义我的回调[o](pr[:销毁o]))
#'用户/我的回调
用户>(销毁对象(使对象成为我的回调))
[:销毁{:析构函数#,:其他数据“数据”}]
无
用户>
此解决方案是否适合您的要求
scratch-clj.first:
(ns scratch-clj.first)
(def listf (atom []))
(def destroy-listeners (atom []))
(def add-listeners (atom []))
(defn add-destroy-listener [f]
(swap! destroy-listeners conj f))
(defn add-add-listener [f]
(swap! add-listeners conj f))
(defn add-object []
(let [o (Object.)]
(doseq [f @add-listeners] (f o))
(swap! listf conj o)
(println @listf)))
(defn remove-object []
(doseq [f @destroy-listeners] (f (first @listf)))
(swap! listf rest)
(println @listf))
一些听众:
(ns scratch-clj.another
(:require [scratch-clj.first :as fst]))
(defn do-something-useful-on-remove [object]
(println "object removed is:" object))
(defn do-something-useful-on-add [object]
(println "object added is:" object))
初始化绑定:
(ns scratch-clj.testit
(require [scratch-clj.another :as another]
[scratch-clj.first :as fst]))
(defn add-listeners []
(fst/add-destroy-listener another/do-something-useful-on-remove)
(fst/add-add-listener another/do-something-useful-on-add))
(defn test-it []
(add-listeners)
(fst/add-object)
(fst/remove-object))
测试:
(测试它)
=>添加的对象是:#
[#]
删除的对象是:#
()
因此,以下是我根据mobytes的建议提出的最终解决方案。还有点工作,但是
我怀疑我将来会想要这个
谢谢你的帮助
;; hook system
(defn make-hook []
(atom []))
(defn add-hook [hook func]
(do
(when-not
(some #{func} @hook)
(swap! hook conj func))
@hook))
(defn remove-hook [hook func]
(swap! hook
(partial
remove #{func})))
(defn clear-hook [hook]
(reset! hook []))
(defn run-hook
([hook]
(doseq [func @hook] (func)))
([hook & rest]
(doseq [func @hook] (apply func rest))))
(defn phils-hook []
(println "Phils hook"))
(defn phils-hook2 []
(println "Phils hook2"))
(def test-hook (make-hook))
(add-hook test-hook phils-hook)
(add-hook test-hook phils-hook2)
(run-hook test-hook)
(remove-hook test-hook phils-hook)
(run-hook test-hook)
这是一个合理的想法,但它只是转移了问题。现在我需要能够钩住make对象函数,以便添加析构函数。以上面的例子为例(在您的评论之后添加,对不起!)--对象创建和销毁发生在命名空间“first”中,而我的回调等价物发生在其他地方。好吧,这样说,问题之一是clojure没有很好地处理对象具有析构函数的想法。如果您将(几乎)所有对象都视为“哑”记录,您通常会玩得更开心。从您的代码中,您不想看到对象是否被销毁,您希望在修改listf中的集合时执行某种操作。手表显然是实现这一点的工具,它们不可能很快被删除。在本例中,所讨论的对象(如我的示例中)是Java对象——我使用clojure来操作Java API。一旦从列表中删除,对象将被销毁。一个参考手表保证被调用?这是不可否决的,是吗?是的,我想是的。我想我也可以用手表来做这件事,但这对我来说似乎更自然,也更一般。非常感谢!嗯,小心“破坏”这个词。我相信只有在GC清理时才会发生对象破坏。移除对象与销毁对象不同,尽管一个可能导致另一个。
;; hook system
(defn make-hook []
(atom []))
(defn add-hook [hook func]
(do
(when-not
(some #{func} @hook)
(swap! hook conj func))
@hook))
(defn remove-hook [hook func]
(swap! hook
(partial
remove #{func})))
(defn clear-hook [hook]
(reset! hook []))
(defn run-hook
([hook]
(doseq [func @hook] (func)))
([hook & rest]
(doseq [func @hook] (apply func rest))))
(defn phils-hook []
(println "Phils hook"))
(defn phils-hook2 []
(println "Phils hook2"))
(def test-hook (make-hook))
(add-hook test-hook phils-hook)
(add-hook test-hook phils-hook2)
(run-hook test-hook)
(remove-hook test-hook phils-hook)
(run-hook test-hook)