在学习 Clojure 的过程中,我遇到了以下问题:
setup:图形数据结构的类,使用 deftype
和 definterface
创建,具有 addNode [id data] 成员函数。直接调用时按预期工作,例如 (.addNode graph "anItem"14)
想法:由于字符串标记化和更新图表都消耗大量时间(至少数百万行),我想连续读取和标记文件并将标记列表推送到一个将执行 `(.addNode graph id data) 部分的代理。
问题:我似乎找不到正确的语法来使代理接受类实例的成员函数作为更新函数。
简化的代码(此处删除了命名空间,可能包含拼写错误!):
; from graph.clj
(definterface IGraph
(addNode [id data])
(addNode2 [_ id data]))
(deftype Graph [^:volatile-mutable nodes] ; expects an empty map, else further calls fail horribly
IGraph
(addNode [this id data] (set! nodes (assoc nodes id data)) this)
(addNode2 [this _ id data] (.addNode this id data) this))
; core.clj
(def g (Graph. {}))
(def smith (agent g)) ; agent smith shall do the dirty work
(send smith .addNode "x" 42) ; unable to resolve symbol
(send smith (.addNode @smith) "x" 42) ; IllegalArgumentException (arity?)
(send smith (.addNode2 @smith) "x" 42) ; same as above. Not arity after all?
(send smith #(.addNode @smith) "x" 42) ; ArityException of eval (3)
(send smith (partial #(.addNode @smith)) "x" 42) ; the same
; agent smith, the president is ashamed...
这五行代码由于各种原因不起作用,而一个简单的
(def jones (agent 0))
(send jones + 1)
; agent jones, this nation is in your debt
执行成功。这应该是可能的,那么我做错了什么?
最佳答案
您的直接问题是 .addNode
不是一个函数,而是围绕 .
特殊形式的一些糖。您无法通过这种方式传递特殊形式,因此您需要将其包装在代理知道如何调用的函数中 - #(.addNode %&)
或类似的东西。只有当所有参数都存在时,才会对特殊形式进行求值,并且它可以看到图上的第一个参数中有一个 addNode
方法。
仍然,James Sharp's answer有一个很好的观点 - 这是处理这个问题的一种非常必要的和面向对象的方法。从到目前为止您的代码来看,您似乎打算使用 send
将列表中的 token 串行馈送到 smith
中,然后由 assoc< 更新他的图表
-ing 每个。这是一个经典的reduce
操作 - 将空图和assoc
放入其中,将其结果和assoc
放入其中,依此类推,直到输入用完。让代理在此过程的每个步骤之间执行 STM 操作似乎并不是很有必要。
如果出于性能原因您希望使用 ^:volatile-mutable
,您还可以尝试使用 transient 并减少 assoc!
- 或者仅使用 (进入 {} ...
,它为您处理 transient (尽管它的行为类似于 conj
,而不是 assoc
,并且对于映射,需要 [ 的向量)键值]
而不是单独的键和值参数)。
关于object - Clojure并发: Let an agent act on a java object/deftype,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31559373/