graph - suptitle - subplot title python
Cómo anular el comportamiento println para tipos de referencia (3)
Lo que quiere hacer es crear un nuevo espacio de nombres y definir sus propias funciones de impresión que modelen la forma en que clojure imprime los objetos, y de manera predeterminada los métodos de clojure.
En detalle:
- Cree un ns excluyendo pr-str y print-method. Cree un método de impresión multimétodo (copie exactamente lo que está en clojure.core) Cree un método predeterminado que simplemente delegue en clojure.core / print-method Cree un método para clojure.lang.Ref que no imprime de manera recursiva todo
Como beneficio adicional, si usa clojure 1.4.0, puede usar literales etiquetados para que la lectura en la salida también sea posible. Debería sobrescribir el mapa *data-readers*
para una etiqueta personalizada y una función que devuelve un objeto ref. También necesitaría anular el comportamiento de la cadena de lectura para asegurarse de que se requiere el enlace para *data-readers*
.
He proporcionado un ejemplo para 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)))
Ahora puede llamar así (println (my.print/pr-str circular-data-structure))
Tengo un gráfico cíclico que creé usando dosync
y ref-set
. Cuando paso esto a println
obtengo un java.lang.StackOverflowError
como era de esperar, porque efectivamente está tratando de imprimir una estructura infinitamente anidada.
Descubrí que si lo hago (str my-ref)
crea algo que se parece a vertex@23f7d873
y no intenta atravesar la estructura e imprimir todo, así que esto resuelve el problema en el sentido inmediato, pero solo ayuda cuando Soy muy cuidadoso con lo que estoy imprimiendo en la pantalla. Me gustaría poder llamar (println my-graph)
hacer que imprima el ref
como algún tipo de texto personalizado (posiblemente involucrando str
), y las otras cosas que no son ref normalmente.
Actualmente tengo una función de impresión personalizada que imprime cada elemento de la estructura por sí mismo y omite por completo la impresión de la ref
. (Resulta que mirar el vertex@23f7d873
realidad no es muy útil). Esto es incómodo de usar y obstaculiza en gran medida la inspección casual de las cosas en el REPL y también evita que el inspector de Emacs mire cosas mientras estoy en una swank.core/break
depuración swank.core/break
.
Un detalle es que la ref
es en realidad un valor en una defstruct
que también contiene algunas otras cosas que estoy tratando de imprimir normalmente.
Así que me pregunto qué camino debería seguir. Veo estas opciones:
-
CharSequence
elextend-type
y aplique el protocoloCharSequence
a mi estructura de estructura para que, cuando se encuentre con unaref
, funcione correctamente. Esto aún requiere una inspección campo por campo de la estructura y un caso especial cuando se trata de laref
, pero al menos localiza el problema en la estructura y no en todo lo que contiene la estructura. -
CharSequence
cómo anular el protocoloCharSequence
cuando se trata de unaref
. Esto permite un comportamiento aún más localizado y me permite ver una referencia cíclica en el REPL incluso cuando no está dentro de una estructura. Esta es mi opción preferida. - Averigua cómo hacer algo con
toString
que creo que se llama en algún nivel cuandoprintln
. Soy muy ignorante sobre esta opción. Bastante ignorante acerca de los otros también, pero he estado leyendoJoy of Clojure
y ahora estoy inspirado.
Del mismo modo, esta solución debería aplicarse a la print
y pprint
y cualquier otra cosa que normalmente fallaría al tratar de imprimir una referencia cíclica. ¿Qué estrategia debería emplear?
muchas gracias por cualquier entrada.
Encontré mi solución: simplemente crea un clojure.core/print-method
multimétodo que sobrecarga el clojure.core/print-method
para cada tipo particular, y llama a la función genérica desde el clojure.core/print-method
multimétodo. En este caso, cada multimétodo llama al print-graph-element
genérico que sabe qué hacer con las referencias (simplemente imprime el hash del objeto al que se hace referencia en lugar de intentar imprimir el valor del objeto al que se hace referencia). En este caso, supongo que la función de envío de print-method es justa (type <thing>)
por lo que se distribuye en tipo, y así es como estoy escribiendo el método multimétodo. De hecho, no pude encontrar ninguna documentación que indique que el envío del método de impresión funciona, pero parece que se comporta de esa manera.
Esta solución me permite simplemente escribir el nombre del objeto, como v0
(que fue cerado por (make-vertex) en el método repl y print, que es llamado por el repl, evitando así mi error de .
(defmethod clojure.core/print-method vertex [v writer]
(print-graph-element v))
(defmethod clojure.core/print-method edge [e writer]
(print-graph-element e))
Si solo desea poder imprimir estructuras de datos con bucles, intente configurar *print-level*
para limitar la profundidad de descenso de la impresora.
user=> (def a (atom nil))
#''user/a
user=> (set! *print-level* 3)
3
user=> (reset! a a)
#<Atom@f9104a: #<Atom@f9104a: #<Atom@f9104a: #>>>
También hay una var *print-length*
que puede ser útil cuando se trata de datos con una longitud infinita.