Отладка в Clojure? [закрытый]

224
задан John Lawrence Aspden 13 February 2013 в 09:33
поделиться

5 ответов

Также есть 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
156
ответ дан 23 November 2019 в 03:56
поделиться

У меня есть небольшой отладочный макрос, который я считаю очень полезным:

;;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)
100
ответ дан 23 November 2019 в 03:56
поделиться

Вы также можете вставить код для перехода в 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.

36
ответ дан 23 November 2019 в 03:56
поделиться

Мой любимый метод - это обильное разбрызгивание 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)
                   ^^^
46
ответ дан 23 November 2019 в 03:56
поделиться

Если вы используете 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

, как упоминалось в комментарии выше.

9
ответ дан 23 November 2019 в 03:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: