2

努力学习 Clojure 我遇到了以下问题:

setup:图形数据结构的类,使用 addNode [id data] 成员函数创建deftypedefinterface具有。直接调用时按预期工作,例如(.addNode graph "anItem" 14)

想法:由于字符串标记化和更新图形都消耗大量时间(至少数百万行),我想连续读取和标记文件并将标记列表推送到将执行`(.addNode图 id 数据)部分。

问题:我似乎找不到正确的语法来使代理接受类实例的成员函数作为更新函数。

简化代码(此处删除了命名空间,可能包含拼写错误!):

; 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

成功执行。这应该是可能的,所以我做错了什么?

4

2 回答 2

2

您的直接问题是这.addNode不是功能,而是.特殊形式周围的一些糖。您不能以这种方式传递特殊形式,因此您需要将其包装在代理知道如何调用的函数中 -#(.addNode %&)或类似的东西。然后只有在所有参数都存在时才评估特殊形式,并且它可以看到addNode图上的第一个参数中有一个方法。

尽管如此,James Sharp 的回答有一个很好的观点——这是一种非常必要且面向对象的方法来处理这个问题。从到目前为止的代码来看,您似乎打算将列表中的令牌连续输入smithwith send,然后他将通过assoc-ing 来更新他的图表。这是一个经典的reduce操作 - 将空图assoc放入其中,将结果assoc放入其中,依此类推,直到输入用完。在此过程的每个步骤之间让代理执行 STM 事情似乎不是很有必要。

如果您^:volatile-mutable出于性能原因希望使用,您也可以尝试使用瞬态和减少assoc!- 或仅使用(into {} ...which 为您处理瞬态(尽管它的行为类似于conj, notassoc并且 for maps 需要向量[key value]而不是单独的键和值参数) .

于 2015-07-22T11:06:39.813 回答
1

您尝试做的事情可能是可能的,但恕我直言,这不是惯用的,您正在以 OO 的方式思考。正如文档所说,

代理本身应该是不可变的(最好是 Clojure 持久集合之一的实例)

您可以使用Map为树建模,看看这个例子

一般来说,4clojure是开始编写惯用的 Clojure 解决方案的好地方

编辑更完整和惯用的例子

于 2015-07-22T09:50:52.887 回答