簡體   English   中英

如何覆蓋引用類型的 println 行為

[英]How to override println behavior for reference types

我有一個使用dosyncref-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 放在什么路徑下。 我看到這些選項:

  1. 找出extend-type並將CharSequence協議應用於我的defstruct ed 結構,以便在遇到ref時它可以正常工作。 這仍然需要逐個字段檢查結構和涉及ref的特殊情況,但至少它將問題定位到結構而不是包含結構的任何內容。
  2. 找出在遇到ref時如何覆蓋CharSequence協議。 這允許更多的本地化行為,並允許我在 REPL 中查看循環引用,即使它不在結構內也是如此。 這是我的首選。
  3. 弄清楚如何使用toString做某事,我相信當我執行println時,它會在某種程度上被調用。 我對這個選項最無知。 對其他人也一無所知,但我一直在閱讀Joy of Clojure ,現在我都受到了啟發。

同樣,此解決方案應適用於printpprint以及在嘗試打印循環引用時通常會出現的任何其他問題。 我應該采用什么策略?

非常感謝任何輸入。

你想要做的是創建一個新的命名空間並定義你自己的打印函數,模擬 clojure 打印對象的方式,並默認為 clojure 的方法。

詳細地:

    創建一個不包括 pr-str 和 print-method 的 ns。 創建一個多方法打印方法(完全復制 clojure.core 中的內容) 創建一個簡單委托給 clojure.core/print-method 的默認方法 創建一個不遞歸打印所有內容的 clojure.lang.Ref 方法

作為獎勵,如果您使用的是 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM