Risposte:
C'è anche dotrace, che consente di esaminare gli ingressi e le uscite delle funzioni selezionate.
(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(dotrace [fib] (fib 3))
produce l'output:
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
In Clojure 1.4, dotrace
è stato spostato:
Hai bisogno della dipendenza:
[org.clojure/tools.trace "0.7.9"]
(require 'clojure.tools.trace)
E devi aggiungere ^: dinamico alla definizione della funzione
(defn ^:dynamic fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
Quindi Bob è di nuovo tuo zio:
(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)
Ho una piccola macro di debug che trovo molto utile:
;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
Puoi inserirlo ovunque tu voglia vedere cosa sta succedendo e quando:
;; 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
.
Il mio metodo preferito è una spolverata liberale di println
s in tutto il codice ... Accenderli e spegnerli è facile grazie alla #_
macro del lettore (che fa leggere il lettore nel seguente modulo, quindi finge di non averlo mai visto). Oppure potresti utilizzare una macro che si espande in un corpo passato o in nil
base al valore di una variabile speciale, ad esempio *debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
Con un (def *debug* false)
in là, questo si espanderà a nil
. Con true
, si espanderà in body
avvolto in a do
.
La risposta accettata a questa domanda SO: Idiomatic Clojure per i rapporti sui progressi? è molto utile durante il debug delle operazioni di sequenza.
Poi c'è qualcosa che è attualmente incompatibile con Swank-clojure 's REPL, ma è troppo bello per non parlare di: debug-repl
. Puoi usarlo in un REPL autonomo, che è facile da ottenere ad es. Con Leiningen ( lein repl
); e se stai lanciando il tuo programma dalla riga di comando, allora porterà il suo REPL proprio nel tuo terminale. L'idea è che puoi rilasciare la debug-repl
macro ovunque tu voglia e far apparire il proprio REPL quando l'esecuzione del programma raggiunge quel punto, con tutti i locali in ambito ecc. Un paio di collegamenti rilevanti: il debug-sostitu di Clojure , il debug di Clojure -repl trucchi , che ne dici di un debug-repl (sul gruppo Clojure Google), debug-repl su Clojars .
swank-clojure fa un lavoro adeguato nel rendere utile il debugger integrato di SLIME quando si lavora con il codice Clojure - notare come i bit irrilevanti dello stacktrace siano disattivati, quindi è facile trovare l'effettivo problema nel codice in fase di debug. Una cosa da tenere a mente è che le funzioni anonime senza "etichette dei nomi" appaiono nello stacktrace senza praticamente alcuna informazione utile allegata; quando viene aggiunto un "nome tag", appare nello stacktrace e tutto va di nuovo bene:
(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)
^^^
Puoi anche inserire il codice per rilasciarti in un REPL con tutti i binding locali, usando 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))))
Quindi per usarlo, inseriscilo ovunque tu voglia avviare il sostituto:
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
Lo inserisco nel mio user.clj in modo che sia disponibile in tutte le sessioni REPL.
"modi migliori per eseguire il debug del codice Clojure, mentre si usa il sost
Leggermente a sinistra, ma "usando la REPL stessa".
Ho scritto l'hobbista Clojure per oltre un anno e non ho sentito il bisogno di strumenti di debug. Se mantieni piccole le tue funzioni ed esegui ognuna con gli input previsti nel REPL e osservi i risultati, dovrebbe essere possibile avere un quadro abbastanza chiaro di come si sta comportando il tuo codice.
Trovo che un debugger sia molto utile per osservare STATE in un'applicazione in esecuzione. Clojure rende semplice (e divertente!) Scrivere in uno stile funzionale con strutture di dati immutabili (senza cambiare stato). Ciò riduce enormemente la necessità di un debugger. Una volta che so che tutti i componenti si comportano come mi aspetto (prestando particolare attenzione ai tipi di cose), il comportamento su larga scala è raramente un problema.
Se usi emacs / slime / swank, prova questo nel REPL:
(defn factorial [n]
(cond (< n 2) n
(= n 23) (swank.core/break)
:else (* n (factorial (dec n)))))
(factorial 30)
Non ti dà una traccia dello stack completo come se fossi sotto LISP, ma è buono per frugare in giro.
Questo è il bel lavoro di:
http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml
come è stato menzionato in un commento sopra.
Per IntelliJ c'è un eccellente plug-in Clojure chiamato Cursive . Tra le altre cose, fornisce un REPL che puoi eseguire in modalità debug e scorrere il tuo codice Clojure proprio come faresti per esempio con Java.
Vorrei secondare la risposta di Peter Westmacott anche se, nella mia esperienza, l'esecuzione di pezzi del mio codice nel REPL è il più delle volte una forma sufficiente di debug.
Leiningen
, mostra:Error running 'ring server': Trampoline must be enabled for debugging
ring
o lein
forse vale la pena pubblicare una domanda separata?
A partire dal 2016 è possibile utilizzare Debux , una semplice libreria di debug per Clojure / Script che funziona in combinazione con il tuo sostituto e la console del tuo browser. È possibile cospargere macro dbg
(debug) o clog
(console.log) nel codice e osservare facilmente i risultati di singole funzioni, ecc., Stampati su REPL e / o console.
Dal file Leggimi del progetto :
Utilizzo di base
Questo è un semplice esempio. La macro dbg stampa un modulo originale e stampa graziosamente il valore valutato nella finestra REPL. Quindi restituisce il valore senza interferire con l'esecuzione del codice.
Se avvolgi il codice con dbg in questo modo,
(* 2 (dbg (+ 10 20))) ; => 60
quanto segue verrà stampato nella finestra REPL.
Uscita REPL:
dbg: (+ 10 20) => 30
Dbg nidificato
La macro dbg può essere nidificata.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
Uscita REPL:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Hugo Duncan e collaboratori continuano a fare un lavoro straordinario con il progetto ritz . Ritz-nrepl è un server nREPL con funzionalità di debug. Guarda i debugger di Hugo in Clojure parlare a Clojure / Conj 2012 per vederlo in azione, nel video alcune diapositive non sono leggibili, quindi potresti voler vedere le diapositive da qui .
Usa spyscope che implementa una macro lettore personalizzata in modo che il tuo codice di debug sia anche codice di produzione https://github.com/dgrnbrg/spyscope
Provenendo da Java e conoscendo Eclipse, mi piace quello che Counterclockwise (il plugin Eclipse per lo sviluppo di Clojure) ha da offrire: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code
Ecco una bella macro per il debug di let
moduli complicati :
(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)))
... e un saggio che spiega il suo uso .
Versione della funzione di def-let, che trasforma un let in una serie di def. Un po 'di credito va a qui
(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)))))
Utilizzo: deve citare il contenuto con un preventivo, ad es
(def-let '[a 1 b 2 c (atom 0)])