[英]How to override println behavior for reference types
我有一個使用dosync
和ref-set
創建的循環圖。 當我將它傳遞給println
時,我得到了一個java.lang.StackOverflowError
正如我所料,因為它實際上是在嘗試打印一個無限嵌套的結構。
我發現如果我這樣做(str my-ref)
它會創建一些看起來像vertex@23f7d873
的東西並且實際上不會嘗試遍歷結構並打印出所有內容,所以這在直接意義上解決了問題,但只有在我對要打印到屏幕上的內容非常小心。 我希望能夠調用(println my-graph)
讓它打印ref
作為某種類型的自定義文本(可能涉及str
),以及其他非 ref 內容。
目前我有一個自定義打印 function,它單獨打印結構的每個元素並完全跳過打印ref
。 (事實證明,查看vertex@23f7d873
實際上並不是很有用)。 這使用起來很尷尬,並且極大地阻礙了在 REPL 中對內容進行隨意檢查,並且還阻止了 Emacs 檢查員在我進行swank.core/break
調試時查看內容。
一個細節是ref
實際上是defstruct
中的一個值,它還包含一些我試圖正常打印的其他內容。
所以我想知道我應該把 go 放在什么路徑下。 我看到這些選項:
extend-type
並將CharSequence
協議應用於我的defstruct
ed 結構,以便在遇到ref
時它可以正常工作。 這仍然需要逐個字段檢查結構和涉及ref
的特殊情況,但至少它將問題定位到結構而不是包含結構的任何內容。ref
時如何覆蓋CharSequence
協議。 這允許更多的本地化行為,並允許我在 REPL 中查看循環引用,即使它不在結構內也是如此。 這是我的首選。toString
做某事,我相信當我執行println
時,它會在某種程度上被調用。 我對這個選項最無知。 對其他人也一無所知,但我一直在閱讀Joy of Clojure
,現在我都受到了啟發。 同樣,此解決方案應適用於print
和pprint
以及在嘗試打印循環引用時通常會出現的任何其他問題。 我應該采用什么策略?
非常感謝任何輸入。
你想要做的是創建一個新的命名空間並定義你自己的打印函數,模擬 clojure 打印對象的方式,並默認為 clojure 的方法。
詳細地:
作為獎勵,如果您使用的是 clojure 1.4.0,則可以使用標記文字,以便也可以讀取 output。 您需要覆蓋自定義標記的*data-readers*
map 和返回 ref object 的 function。您還需要覆蓋讀取字符串行為以確保為*data-readers*
調用綁定。
我已經為 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)))
現在你可以這樣調用(println (my.print/pr-str circular-data-structure))
我找到了我的解決方案——只需創建一個為每個特定類型重載clojure.core/print-method
的多方法,並從多方法中調用通用 function。 在這種情況下,每個 multimethod 調用知道如何處理引用的通用print-graph-element
(它只是打印出引用的 object 的 hash,而不是嘗試打印出引用對象的值)。 在這種情況下,我假設 print-method 的調度 function 只是(type <thing>)
所以它按類型調度,這就是我編寫 multimethod 的方式。 我實際上找不到任何文檔說明打印方法調度是如何工作的,但它確實看起來是那樣的。
該解決方案允許我僅鍵入 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))
如果您只想能夠打印帶有循環的數據結構,請嘗試設置*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: #>>>
還有一個*print-length*
變量,當你處理無限長度的數據時它會很有用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.