Respuestas:
También hay dotrace, que le permite ver las entradas y salidas de las funciones seleccionadas.
(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(dotrace [fib] (fib 3))
produce la salida:
TRACE t4425: (fib 3)
TRACE t4426: | (fib 2)
TRACE t4427: | | (fib 1)
TRACE t4427: | | => 1
TRACE t4428: | | (fib 0)
TRACE t4428: | | => 0
TRACE t4426: | => 1
TRACE t4429: | (fib 1)
TRACE t4429: | => 1
TRACE t4425: => 2
2
En Clojure 1.4, se dotrace
ha movido:
Necesitas la dependencia:
[org.clojure/tools.trace "0.7.9"]
(require 'clojure.tools.trace)
Y necesita agregar ^: dinámica a la definición de la función
(defn ^:dynamic fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
Entonces Bob es una vez más tu tío:
(clojure.tools.trace/dotrace [fib] (fib 3))
TRACE t4328: (fib 3)
TRACE t4329: | (fib 2)
TRACE t4330: | | (fib 1)
TRACE t4330: | | => 1
TRACE t4331: | | (fib 0)
TRACE t4331: | | => 0
TRACE t4329: | => 1
TRACE t4332: | (fib 1)
TRACE t4332: | => 1
TRACE t4328: => 2
user=> (use 'closure.contrib.trace) java.io.FileNotFoundException: Could not locate closure/contrib/trace__init.class or closure/contrib/trace.clj on classpath: (NO_SOURCE_FILE:0)
Tengo una pequeña macro de depuración que me parece muy útil:
;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
Puede insertarlo donde quiera ver lo que sucede y cuándo:
;; Examples of dbg
(println (+ (* 2 3) (dbg (* 8 9))))
(println (dbg (println "yo")))
(defn factorial[n] (if (= n 0) 1 (* n (dbg (factorial (dec n))))))
(factorial 8)
(def integers (iterate inc 0))
(def squares (map #(dbg(* % %)) integers))
(def cubes (map #(dbg(* %1 %2)) integers squares))
(take 5 cubes)
(take 5 cubes)
clojure.tools.trace/trace
.
Mi método favorito es una pizca liberal de println
s en todo el código ... Activarlos y desactivarlos es fácil gracias a la #_
macro del lector (que hace que el lector lea de la siguiente forma, y luego finja que nunca lo ha visto). O podría usar una macro expandiéndose a un cuerpo pasado o nil
dependiendo del valor de alguna variable especial, por ejemplo *debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
Con un (def *debug* false)
allí, esto se expandirá a nil
. Con true
, se expandirá a body
envuelto en un do
.
La respuesta aceptada a esta pregunta SO: ¿ Idiomatic Clojure para informes de progreso? es muy útil al depurar operaciones de secuencia.
Entonces hay algo que es actualmente incompatible con Swank-clojure 's REPL, pero es demasiado bueno para no mencionar: debug-repl
. Puede usarlo en un REPL independiente, que es fácil de obtener, por ejemplo, con Leiningen ( lein repl
); y si está iniciando su programa desde la línea de comando, entonces traerá su propia REPL directamente en su terminal. La idea es que puede soltar la debug-repl
macro en cualquier lugar que desee y hacer que aparezca su propio REPL cuando la ejecución del programa llegue a ese punto, con todos los locales en el alcance, etc. Un par de enlaces relevantes: Clojure debug-repl , Clojure debug -repl trucos , qué tal un debug-repl (en el grupo Clojure Google), debug-repl en Clojars .
swank-clojure hace un trabajo adecuado al hacer que el depurador incorporado de SLIME sea útil cuando se trabaja con código Clojure; tenga en cuenta cómo los bits irrelevantes del stacktrace están atenuados para que sea fácil encontrar el problema real en el código que se está depurando. Una cosa a tener en cuenta es que las funciones anónimas sin "etiquetas de nombre" aparecen en el stacktrace básicamente sin información útil adjunta; cuando se agrega una "etiqueta de nombre", aparece en el stacktrace y todo vuelve a estar bien:
(fn [& args] ...)
vs.
(fn tag [& args] ...)
example stacktrace entries:
1: user$eval__3130$fn__3131.invoke(NO_SOURCE_FILE:1)
vs. ^^
1: user$eval__3138$tag__3139.invoke(NO_SOURCE_FILE:1)
^^^
También puede insertar código para dejarse caer en un REPL con todos los enlaces locales, utilizando Alex Osbornedebug-repl
:
(defmacro local-bindings
"Produces a map of the names of local bindings to their values."
[]
(let [symbols (map key @clojure.lang.Compiler/LOCAL_ENV)]
(zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols)))
(declare *locals*)
(defn eval-with-locals
"Evals a form with given locals. The locals should be a map of symbols to
values."
[locals form]
(binding [*locals* locals]
(eval
`(let ~(vec (mapcat #(list % `(*locals* '~%)) (keys locals)))
~form))))
(defmacro debug-repl
"Starts a REPL with the local bindings available."
[]
`(clojure.main/repl
:prompt #(print "dr => ")
:eval (partial eval-with-locals (local-bindings))))
Luego, para usarlo, insértelo donde quiera que comience la respuesta:
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
Pego esto en mi user.clj para que esté disponible en todas las sesiones REPL.
"Las mejores formas de depurar el código Clojure, mientras se usa la respuesta"
Ligeramente a la izquierda, pero "usando el REPL por sí mismo".
Llevo más de un año escribiendo al aficionado Clojure y no he sentido una gran necesidad de herramientas de depuración. Si mantiene sus funciones pequeñas y ejecuta cada una con las entradas esperadas en el REPL y observa los resultados, entonces debería ser posible tener una imagen bastante clara de cómo se comporta su código.
Creo que un depurador es más útil para observar ESTADO en una aplicación en ejecución. Clojure hace que sea fácil (¡y divertido!) Escribir en un estilo funcional con estructuras de datos inmutables (sin cambios de estado). Esto reduce enormemente la necesidad de un depurador. Una vez que sé que todos los componentes se comportan como esperaba (prestando especial atención a los tipos de cosas), el comportamiento a gran escala rara vez es un problema.
Si usa emacs / slime / swank, intente esto en REPL:
(defn factorial [n]
(cond (< n 2) n
(= n 23) (swank.core/break)
:else (* n (factorial (dec n)))))
(factorial 30)
No te da un seguimiento completo de la pila como si estuvieras bajo LISP, pero es bueno para hurgar.
Este es el buen trabajo de:
http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml
como se mencionó en un comentario anterior.
Para IntelliJ hay un excelente complemento de Clojure llamado Cursive . Entre otras cosas, proporciona un REPL que puede ejecutar en modo de depuración y recorrer su código Clojure tal como lo haría, por ejemplo, Java.
Sin embargo, respaldaría la respuesta de Peter Westmacott porque, según mi experiencia, solo ejecutar partes de mi código en el REPL es la mayoría de las veces una forma suficiente de depuración.
Leiningen
, muestra:Error running 'ring server': Trampoline must be enabled for debugging
ring
o lein
, ¿quizás valga la pena publicar una pregunta por separado?
A partir de 2016, puede utilizar Debux , una biblioteca de depuración simple para Clojure / Script que funciona junto con su respuesta, así como la consola de su navegador. Puede rociar dbg
(depurar) o clog
macros (console.log) en su código y observar fácilmente los resultados de funciones individuales, etc., impresas en su REPL y / o consola.
Del archivo Léame del proyecto :
Uso básico
Este es un ejemplo simple. La macro dbg imprime un formulario original e imprime el valor evaluado en la ventana REPL. Luego devuelve el valor sin interferir con la ejecución del código.
Si envuelve el código con dbg como este,
(* 2 (dbg (+ 10 20))) ; => 60
lo siguiente se imprimirá en la ventana REPL.
Salida REPL:
dbg: (+ 10 20) => 30
Dbg anidado
La macro dbg se puede anidar.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
Salida REPL:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Hugo Duncan y sus colaboradores continúan haciendo un trabajo increíble con el proyecto ritz . Ritz-nrepl es un servidor nREPL con capacidades de depuración. Vea la charla de Hugo's Debuggers en Clojure en Clojure / Conj 2012 para verlo en acción, en el video algunas de las diapositivas no son legibles, por lo que es posible que desee ver las diapositivas desde aquí .
Use spyscope que implementa una macro de lector personalizado para que su código de depuración también sea código de producción https://github.com/dgrnbrg/spyscope
Viniendo de Java y familiarizado con Eclipse, me gusta lo que ofrece Counterclockwise (el complemento Eclipse para el desarrollo de Clojure): http://doc.ccw-ide.org/documentation.html#_debug_clojure_code
Aquí hay una buena macro para depurar let
formas complicadas :
(defmacro def+
"def with binding (def+ [{:keys [a b d]} {:a 1 :b 2 :d 3}])"
[bindings]
(let [let-expr (macroexpand `(let ~bindings))
vars (filter #(not (.contains (str %) "__"))
(map first (partition 2 (second let-expr))))
def-vars (map (fn [v] `(def ~v ~v)) vars)]
(concat let-expr def-vars)))
... y un ensayo que explica su uso .
Versión funcional de def-let, que convierte un let en una serie de defs. Un poco de crédito va hasta aquí
(defn def-let [aVec]
(if-not (even? (count aVec))
aVec
(let [aKey (atom "")
counter (atom 0)]
(doseq [item aVec]
(if (even? @counter)
(reset! aKey item)
(intern *ns* (symbol @aKey) (eval item)))
; (prn item)
(swap! counter inc)))))
Uso: necesita citar el contenido con una cita, p. Ej.
(def-let '[a 1 b 2 c (atom 0)])