[英]How to override println behavior for reference types
I have a cyclic graph I created using dosync
and ref-set
.我有一个使用
dosync
和ref-set
创建的循环图。 When I pass this to println
I get a java.lang.StackOverflowError
as I would expect, because it's effectively trying to print an infinitely-nested structure.当我将它传递给
println
时,我得到了一个java.lang.StackOverflowError
正如我所料,因为它实际上是在尝试打印一个无限嵌套的结构。
I found that if I do (str my-ref)
it creates something that looks like vertex@23f7d873
and doesn't actually try to traverse the structure and print everything out, so this solves the problem in the immediate sense, but only helps when I'm very careful about what I'm printing to the screen.我发现如果我这样做
(str my-ref)
它会创建一些看起来像vertex@23f7d873
的东西并且实际上不会尝试遍历结构并打印出所有内容,所以这在直接意义上解决了问题,但只有在我对要打印到屏幕上的内容非常小心。 I'd like to be able to call (println my-graph)
have it print the ref
as some type of custom text (possibly involving str
), and the other non-ref stuff normally.我希望能够调用
(println my-graph)
让它打印ref
作为某种类型的自定义文本(可能涉及str
),以及其他非 ref 内容。
Currently I have a custom print function that prints each element of the struct on its own and completely skips printing the ref
.目前我有一个自定义打印 function,它单独打印结构的每个元素并完全跳过打印
ref
。 (It turns out that looking at vertex@23f7d873
is not actually very useful). (事实证明,查看
vertex@23f7d873
实际上并不是很有用)。 This is awkward to use and hinders greatly doing casual inspection of stuff at the REPL and also prevents Emacs inspector from looking at stuff while I'm in a swank.core/break
debug thingy.这使用起来很尴尬,并且极大地阻碍了在 REPL 中对内容进行随意检查,并且还阻止了 Emacs 检查员在我进行
swank.core/break
调试时查看内容。
One detail is the ref
is actually a value in a defstruct
that also contains some other stuff which I am trying to print normally.一个细节是
ref
实际上是defstruct
中的一个值,它还包含一些我试图正常打印的其他内容。
So I'm wondering what path I should go down.所以我想知道我应该把 go 放在什么路径下。 I see these options:
我看到这些选项:
extend-type
and apply the CharSequence
protocol to my defstruct
ed structure so that when it comes across a ref
it works properly.extend-type
并将CharSequence
协议应用于我的defstruct
ed 结构,以便在遇到ref
时它可以正常工作。 This still requires a field-by-field inspection of the struct and a special case when it comes to the ref
, but at least it localizes the problem to the struct and not to anything that contains the struct.ref
的特殊情况,但至少它将问题定位到结构而不是包含结构的任何内容。CharSequence
protocol when it comes across a ref
.ref
时如何覆盖CharSequence
协议。 This allows even more localized behavior and allows me to view a cyclic ref at the REPL even when it's not inside a struct.toString
which I believe is called at some level when I do println
.toString
做某事,我相信当我执行println
时,它会在某种程度上被调用。 I'm most ignorant about this option.Joy of Clojure
and I'm all inspired now.Joy of Clojure
,现在我都受到了启发。 Likewise this solution should apply to print
and pprint
and anything else that would normally barf when trying to print a cyclic ref.同样,此解决方案应适用于
print
和pprint
以及在尝试打印循环引用时通常会出现的任何其他问题。 What strategy should I employ?我应该采用什么策略?
thanks a lot for any input.非常感谢任何输入。
What you want to do is create a new namespace and define your own print functions that models the way clojure prints objects, and defaults to clojure's methods.你想要做的是创建一个新的命名空间并定义你自己的打印函数,模拟 clojure 打印对象的方式,并默认为 clojure 的方法。
In detail:详细地:
As a bonus, if you are using clojure 1.4.0, you can use tagged literals so that reading in the output is also possible.作为奖励,如果您使用的是 clojure 1.4.0,则可以使用标记文字,以便也可以读取 output。 You would need to override the
*data-readers*
map for a custom tag and a function that returns a ref object. You'd also need to override the read-string behavior to ensure binding is called for *data-readers*
.您需要覆盖自定义标记的
*data-readers*
map 和返回 ref object 的 function。您还需要覆盖读取字符串行为以确保为*data-readers*
调用绑定。
I've provided an example for java.io.File我已经为 java.io.File 提供了一个示例
(ns my.print
(:refer-clojure :exclude [pr-str read-string print-method]))
(defmulti print-method (fn [x writer]
(class x)))
(defmethod print-method :default [o ^java.io.Writer w]
(clojure.core/print-method o w))
(defmethod print-method java.io.File [o ^java.io.Writer w]
(.write w "#myprint/file \"")
(.write w (str o))
(.write w "\""))
(defn pr-str [obj]
(let [s (java.io.StringWriter.)]
(print-method obj s)
(str s)))
(defonce reader-map
(ref {'myprint/file (fn [arg]
(java.io.File. arg))}))
(defmacro defdata-reader [sym args & body]
`(dosync
(alter reader-map assoc '~sym (fn ~args ~@body))))
(defn read-string [s]
(binding [*data-readers* @reader-map]
(clojure.core/read-string s)))
Now you can call like so (println (my.print/pr-str circular-data-structure))
现在你可以这样调用
(println (my.print/pr-str circular-data-structure))
I found my solution -- just create a multimethod which overloads clojure.core/print-method
for each particular type, and call the generic function from within the multimethod.我找到了我的解决方案——只需创建一个为每个特定类型重载
clojure.core/print-method
的多方法,并从多方法中调用通用 function。 In this case, each multimethod calls the generic print-graph-element
which knows what to do with references (it just prints out the hash of the referenced object rather than trying to print out the value of the referenced object).在这种情况下,每个 multimethod 调用知道如何处理引用的通用
print-graph-element
(它只是打印出引用的 object 的 hash,而不是尝试打印出引用对象的值)。 In this case I'm assuming the dispatch function of print-method is just (type <thing>)
so it dispatches on type, and that's how I'm writing the multimethod.在这种情况下,我假设 print-method 的调度 function 只是
(type <thing>)
所以它按类型调度,这就是我编写 multimethod 的方式。 I actually couldn't find any documentation that that's how the print-method dispatch works, but it sure seems to behave that way.我实际上找不到任何文档说明打印方法调度是如何工作的,但它确实看起来是那样的。
This solution allows me to just type the name of the object, such as v0
(which was cerated by (make-vertex) in the repl and print-method gets called by the repl, thus preventing my StackOverflow error.该解决方案允许我仅键入 object 的名称,例如
v0
(由 repl 中的 (make-vertex) 生成,repl 调用打印方法,从而防止我的 StackOverflow 错误。
(defmethod clojure.core/print-method vertex [v writer]
(print-graph-element v))
(defmethod clojure.core/print-method edge [e writer]
(print-graph-element e))
If you just want to be able to print data structures with loops, try setting *print-level*
to limit how deep the printer will descend.如果您只想能够打印带有循环的数据结构,请尝试设置
*print-level*
以限制打印机下降的深度。
user=> (def a (atom nil))
#'user/a
user=> (set! *print-level* 3)
3
user=> (reset! a a)
#<Atom@f9104a: #<Atom@f9104a: #<Atom@f9104a: #>>>
There is also a *print-length*
var which can be useful when you are dealing with data with infinite length.还有一个
*print-length*
变量,当你处理无限长度的数据时它会很有用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.