Respostas:
Há também o dotrace, que permite observar as entradas e saídas das funções selecionadas.
(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(dotrace [fib] (fib 3))
produz a saída:
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
No Clojure 1.4, dotrace
mudou:
Você precisa da dependência:
[org.clojure/tools.trace "0.7.9"]
(require 'clojure.tools.trace)
E você precisa adicionar a dinâmica ^: à definição da função
(defn ^:dynamic fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
Então Bob é mais uma vez seu tio:
(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)
Eu tenho uma pequena macro de depuração que acho muito útil:
;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
Você pode inseri-lo onde quiser assistir o que está acontecendo 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
.
Meu método favorito é uma aspersão liberal de println
s por todo o código ... É fácil ativá-los e desativá-los graças à #_
macro do leitor (que faz com que o leitor leia da seguinte forma, depois finja que nunca o viu). Ou você pode usar uma macro expandindo para um corpo passado ou nil
dependendo do valor de alguma variável especial, digamos *debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
Com um (def *debug* false)
, isso será expandido para nil
. Com true
, ele será expandido para body
envolto em um do
.
A resposta aceita para esta pergunta do SO: Cloneure Idiomatic para relatórios de progresso? é muito útil ao depurar operações de sequência.
Então há algo que é atualmente incompatível com Swank-clojure 's REPL, mas é bom demais para não mencionar: debug-repl
. Você pode usá-lo em um REPL independente, o que é fácil de obter, por exemplo, com Leiningen ( lein repl
); e se você estiver iniciando seu programa a partir da linha de comando, ele trará o seu próprio REPL diretamente no seu terminal. A idéia é que você pode soltar a debug-repl
macro em qualquer lugar que desejar e exibir a sua própria REPL quando a execução do programa atingir esse ponto, com todos os locais no escopo etc. Alguns links relevantes: O Clojure debug-repl , o Clojure debug -repl truques , que tal um depurador-repl (no grupo Clojure Google), depurador-repl em Clojars .
O swank-clojure faz um trabalho adequado para tornar o depurador interno do SLIME útil ao trabalhar com o código Clojure - observe como os bits irrelevantes do stacktrace ficam acinzentados, para que seja fácil encontrar o problema real no código que está sendo depurado. Um aspecto a ter em mente é que funções anônimas sem "tags de nome" aparecem no rastreamento de pilha com basicamente nenhuma informação útil anexada a elas; Quando uma "tag de nome" é adicionada, ela aparece no rastreamento de pilha e tudo está bem novamente:
(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)
^^^
Você também pode inserir código para se inserir em um REPL com todas as ligações locais, usando o de 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))))
Em seguida, para usá-lo, insira-o onde quiser que a repl seja iniciada:
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
Eu colo isso no meu user.clj para que fique disponível em todas as sessões do REPL.
"melhores maneiras de depurar o código Clojure, enquanto usa o repl"
Campo ligeiramente esquerdo, mas 'usando o próprio REPL'.
Escrevo o hobby de Clojure há mais de um ano e não sinto grande necessidade de ferramentas de depuração. Se você mantiver suas funções pequenas e executar cada uma delas com as entradas esperadas no REPL e observar os resultados, será possível ter uma imagem bastante clara de como seu código está se comportando.
Acho que um depurador é mais útil para observar STATE em um aplicativo em execução. O Clojure torna fácil (e divertido!) Escrever em um estilo funcional com estruturas de dados imutáveis (sem alteração de estado). Isso reduz enormemente a necessidade de um depurador. Quando eu sei que todos os componentes se comportam como eu esperava (prestando atenção especial aos tipos de coisas), o comportamento em larga escala raramente é um problema.
Se você usa o emacs / slime / swank, tente isso no REPL:
(defn factorial [n]
(cond (< n 2) n
(= n 23) (swank.core/break)
:else (* n (factorial (dec n)))))
(factorial 30)
Ele não fornece um rastreamento completo da pilha como você obteria no LISP, mas é bom para bisbilhotar.
Este é o bom trabalho de:
http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml
como foi mencionado em um comentário acima.
Para o IntelliJ, existe um excelente plugin Clojure chamado Cursive . Entre outras coisas, ele fornece um REPL que você pode executar no modo de depuração e percorrer seu código Clojure como faria para, por exemplo, Java.
Eu preferiria a resposta de Peter Westmacott, embora na minha experiência apenas executar partes do meu código no REPL seja, na maioria das vezes, uma forma suficiente de depuração.
Leiningen
, mostra:Error running 'ring server': Trampoline must be enabled for debugging
ring
ou lein
talvez valha a pena postar uma pergunta separada?
A partir de 2016, você poderá usar o Debux , uma biblioteca de depuração simples para Clojure / Script que funciona em conjunto com seu repl e com o console do navegador. Você pode polvilhar dbg
(depurar) ou clog
(console.log) macros no seu código e observar facilmente os resultados de funções individuais, etc., impressas no seu REPL e / ou console.
No Leiame do projeto :
Uso básico
Este é um exemplo simples. A macro dbg imprime um formulário original e imprime o valor avaliado na janela REPL. Em seguida, ele retorna o valor sem interferir na execução do código.
Se você quebrar o código com dbg assim,
(* 2 (dbg (+ 10 20))) ; => 60
o seguinte será impresso na janela REPL.
Saída REPL:
dbg: (+ 10 20) => 30
Dbg aninhado
A macro dbg pode ser aninhada.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
Saída REPL:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Hugo Duncan e colaboradores continuam fazendo um trabalho incrível com o projeto ritz . O Ritz-nrepl é um servidor nREPL com recursos de depuração. Assista a Debuggers de Hugo em Clojure conversando no Clojure / Conj 2012 para vê-lo em ação. No vídeo, alguns dos slides não são legíveis, portanto, você pode querer vê-los daqui .
Use o spyscope que implementa uma macro de leitor personalizada para que seu código de depuração também seja código de produção https://github.com/dgrnbrg/spyscope
Vindo de Java e familiarizado com o Eclipse, gosto do que Counterclockwise (o plug-in do Eclipse para desenvolvimento do Clojure) tem a oferecer: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code
Aqui está uma boa macro para depurar let
formulários complicados :
(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 um ensaio explicando seu uso .
Versão da função def-let, que transforma um let em uma série de defs. Algum crédito vai para aqui
(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: precisa citar o conteúdo com uma cotação, por exemplo
(def-let '[a 1 b 2 c (atom 0)])