Ответы:
Также есть 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
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)
У меня есть небольшой макрос отладки, который я считаю очень полезным:
;;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)
clojure.tools.trace/trace
.
Мой любимый метод - либеральное разбрасывание println
s по всему коду ... Включать и выключать их легко благодаря #_
макросу читателя (который заставляет читателя читать в следующем виде, а затем притворяться, что его никогда не видели). Или вы можете использовать макрос, расширяющийся либо до переданного тела, либо в nil
зависимости от значения некоторой специальной переменной, скажем *debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
С (def *debug* false)
там, это расширится до nil
. Причём true
, он расширится, чтобы body
завернуться в do
.
Принятый ответ на этот SO вопрос: идиоматическая Clojure для отчетов о прогрессе? очень полезно при отладке последовательностей операций.
Тогда есть то , что в настоящее время несовместимо с шикарным-Clojure «s REPL, но слишком хорошо , чтобы не упомянуть: debug-repl
. Вы можете использовать его в автономном REPL, который легко получить, например, с помощью Leiningen ( lein repl
); и если вы запускаете вашу программу из командной строки, то она выведет свой собственный REPL прямо в вашем терминале. Идея состоит в том, что вы можете debug-repl
оставить макрос в любом месте и вызвать его собственный REPL, когда выполнение программы достигнет этой точки, со всеми локальными объектами в области видимости и т. Д. Несколько соответствующих ссылок: Clojure debug-repl , Clojure debug -relp трюки , как насчет debug-repl (в группе Google Clojure), debug-repl на 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)
^^^
Вы также можете вставить код, чтобы добавить себя в REPL со всеми локальными привязками, используя 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))))
Затем, чтобы использовать его, вставьте его туда, где вы хотите, чтобы repl запускался:
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
Я вставляю это в мой user.clj, чтобы он был доступен во всех сеансах REPL.
«Лучшие способы отладки кода Clojure при использовании repl»
Слегка влево, но «используя сам REPL».
Я писал для Clojure более года и не чувствовал особой нужды в каких-либо средствах отладки. Если вы сохраняете свои функции небольшими, и запускаете каждую из них с ожидаемыми входными данными в REPL и наблюдаете результаты, то должно быть возможно иметь довольно четкое представление о том, как ведет себя ваш код.
Я считаю, что отладчик наиболее полезен для наблюдения за состоянием в запущенном приложении. Clojure позволяет легко (и весело!) Писать в функциональном стиле с неизменными структурами данных (без изменения состояния). Это значительно снижает необходимость в отладчике. Как только я узнаю, что все компоненты ведут себя так, как я ожидаю (обращая особое внимание на типы вещей), тогда крупномасштабное поведение редко становится проблемой.
Если вы используете 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
как было упомянуто в комментарии выше.
Для IntelliJ есть отличный плагин Clojure под названием Cursive . Помимо прочего, он предоставляет REPL, который вы можете запускать в режиме отладки и выполнять пошаговый просмотр кода Clojure, как, например, для Java.
Я бы поддержал ответ Питера Уэстмакотта, хотя, по моему опыту, просто выполнение фрагментов моего кода в REPL в большинстве случаев является достаточной формой отладки.
Leiningen
, это показывает:Error running 'ring server': Trampoline must be enabled for debugging
ring
или, lein
возможно, стоит опубликовать отдельный вопрос?
С 2016 года вы можете использовать Debux , простую библиотеку отладки для Clojure / Script, которая работает как с вашим реплеем, так и с консолью вашего браузера. Вы можете посыпать dbg
(отладить) или clog
(console.log) макросы в своем коде и легко наблюдать результаты отдельных функций и т. Д., Напечатанные на ваш REPL и / или консоль.
Из файла Readme проекта :
Основное использование
Это простой пример. Макрос dbg печатает оригинальную форму и довольно печатает оцененное значение в окне REPL. Затем он возвращает значение, не мешая выполнению кода.
Если вы оберните код с dbg, как это,
(* 2 (dbg (+ 10 20))) ; => 60
следующее будет напечатано в окне REPL.
REPL выход:
dbg: (+ 10 20) => 30
Вложенная база данных
Макрос dbg может быть вложенным.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
REPL выход:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Хьюго Дункан и его коллеги продолжают удивительную работу с проектом Ritz . Ritz-nrepl - это сервер nREPL с возможностями отладки. Посмотрите выступление Хьюго « Отладчики в Clojure» на Clojure / Conj 2012, чтобы увидеть его в действии. В видео некоторые слайды не читаются, поэтому вы можете просмотреть слайды отсюда .
Используйте spyscope, который реализует пользовательский макрос для чтения, так что ваш код отладки также является рабочим кодом https://github.com/dgrnbrg/spyscope
Исходя из Java и будучи знакомым с Eclipse, мне нравится то, что Counterclockwise (плагин Eclipse для разработки Clojure) может предложить: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code
Вот хороший макрос для отладки сложных let
форм:
(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)))
Версия функции def-let, которая превращает let в серию определений. Некоторый кредит идет сюда
(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)))))
Использование: Необходимо заключить в кавычки содержимое, например
(def-let '[a 1 b 2 c (atom 0)])