3

我在 refs 中有两张地图,并希望在一次交易中将它们相互关联。

我的功能如下所示:

(defn assoc-two
  [one two]
  (let [newone (assoc @one :two two)
        newtwo (assoc @two :one one)]
    (ref-set one newone)
    (ref-set two newtwo)))

现在我这样打电话assoc-two

(dosync (assoc-two (ref {}) (ref {})))

在这一点上,我得到了 StackOverflowError 。

我也试过这个:

(defn alter-two
  [one two]
  (alter one assoc :two two)
  (alter two assoc :one one))

是否有办法以one具有条目引用的方式执行此操作,two反之亦然并且仍在一个事务中?

4

2 回答 2

6

除非您尝试打印循环引用之一,例如在 REPL 中,否则不会发生堆栈溢出。

so.core=> (def a (ref {}))
#'so.core/a
so.core=> (def b (ref {}))
#'so.core/b
so.core=> (do (dosync (alter-two ab)) :success)
:成功
so.core=> (= b (:two @a))
真的
so.core=> (= a (:one @b))
真的

显然,像这样打印循环引用的对象会有问题,但请参阅这个最近的问题和答案,了解禁用引用类型内容的默认打印

(remove-method print-method clojure.lang.IDeref)

(dosync (alter-two (ref {}) (ref {})))
;=> {:one #<Ref clojure.lang.Ref@7f1f91ac>} 
;   (prints the second ref without attempting to print its circular contents)
于 2014-08-21T18:06:49.683 回答
2

答案可以在下面的几篇文章中找到,这只是 REPL 试图打印你的递归结构。您必须删除打印方法:

(remove-method print-method clojure.lang.IDeref)

或者添加处理您的特定情况的打印方法,此方法必须比常见的更具体clojure.lang.IDeref

于 2014-08-21T18:09:51.073 回答