Также есть dotrace, который позволяет вам просматривать входы и выходы выбранных функций.
(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(dotrace [fib] (fib 3))
производит вывод:
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
В Clojure 1.4, dotrace
перемещен:
Вам нужна зависимость:
[org.clojure/tools.trace "0.7.9"]
(require 'clojure.tools.trace)
И вам нужно добавить ^: dynamic в определение функции
(defn ^:dynamic fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
Тогда Боб снова ваш дядя:
(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
У меня есть небольшой отладочный макрос, который я считаю очень полезным:
;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
Вы можете вставить его везде, где хотите посмотреть, что и когда происходит:
;; 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)
Вы также можете вставить код для перехода в REPL со всеми локальными привязками, используя команду debug-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))))
Then чтобы использовать его, вставьте его туда, где вы хотите, чтобы ответ запускался:
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
Я вставляю его в свой user.clj, поэтому он доступен во всех сеансах REPL.
Мой любимый метод - это обильное разбрызгивание println
ов по всему коду... Включать и выключать их легко благодаря макросу #_
reader (который заставляет читателя прочитать следующую форму, а затем сделать вид, что он ее никогда не видел). Или вы можете использовать макрос, расширяющийся либо до переданного тела, либо до nil
в зависимости от значения специальной переменной, скажем *debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
При наличии (def *debug* false)
это расширится до nil
. При true
он расширится до body
, обернутого в do
.
Принятый ответ на этот вопрос SO: Идиоматический Clojure для отчетов о ходе выполнения? очень полезен при отладке последовательных операций.
Затем есть кое-что, что в настоящее время несовместимо с REPL swank-clojure, но слишком хорошо, чтобы не упомянуть: debug-repl
. Вы можете использовать его в отдельном REPL, который легко получить, например, с помощью Leiningen (lein repl
); а если вы запускаете свою программу из командной строки, то она вызовет свой собственный REPL прямо в вашем терминале. Идея заключается в том, что вы можете бросить макрос debug-repl
в любое удобное для вас место и заставить его вызвать собственный REPL, когда выполнение программы достигнет этой точки, со всеми локалями в области видимости и т. д. Пара соответствующих ссылок: The Clojure debug-repl, Clojure debug-repl tricks, how 'bout a debug-repl (в группе Clojure Google), debug-repl on Clojars.
swank-clojure делает адекватную работу, чтобы сделать встроенный отладчик SLIME полезным при работе с кодом на Clojure - обратите внимание, как неактуальные части стек-трейса закрашиваются серым цветом, чтобы было легко найти реальную проблему в отлаживаемом коде. Следует помнить, что анонимные функции без "тегов имен" появляются в стектрейсе практически без полезной информации; когда добавляется "тег имени", он появляется в стектрейсе, и все снова хорошо:
(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)
^^^
Если вы используете emacs / slime / swank, то попробуйте это в REPL:
(defn factorial [n]
(cond (< n 2) n
(= n 23) (swank.core/break)
:else (* n (factorial (dec n)))))
(factorial 30)
Он не дает вам полной трассировки стека, как в LISP, но он хорош для совершения вокруг.
Это прекрасная работа:
http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml
, как упоминалось в комментарии выше.