Відповіді:
Існує також 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)
І вам потрібно додати ^: динамику до визначення функції
(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
.
Прийнята відповідь на це питання ТА : Ідіоматичне клоджурство для звітування про прогрес? дуже корисно при налагодженні послідовних операцій.
Тоді є те , що в даний час є несумісним з шикарним-Clojure «s REPL, але занадто добре , щоб не згадати: debug-repl
. Ви можете використовувати його в автономній REPL, яку легко отримати, наприклад, з Leiningen ( lein repl
); і якщо ви запускаєте свою програму з командного рядка, вона збирається піднести власну REPL прямо у ваш термінал. Ідея полягає в тому, що ви можете кинути debug-repl
макрос в будь-яке місце, де вам подобається, і він зможе створити свою власну REPL, коли виконання програми досягне цієї точки, з усіма локальними обсягами тощо. Пара відповідних посилань: налагодження Clojure-repl , налагодження Clojure -репл-трюки , як 'debug-repl (у групі Clojure Google), 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 з усіма локальними прив'язками, використовуючи Алекса Осборна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))))
Потім, щоб використовувати його, вставляйте його там, де ви хочете розпочати репл:
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
Я вставлю це у свій user.clj, щоб воно було доступне у всіх сесіях REPL.
"кращі способи налагодження коду Clojure, використовуючи repl"
Трохи ліве поле, але "використовуючи REPL-сам".
Я пишу про любителя Clojure більше року і не відчував великої потреби в будь-яких інструментах налагодження. Якщо ви зберігаєте свої функції невеликими та запускаєте кожну із очікуваними входами на REPL та спостерігаєте за результатами, тоді має бути можливість мати досить чітке уявлення про те, як поводиться ваш код.
Я вважаю, що налагоджувач є найбільш корисним для спостереження за STATE у запущеній програмі. Clojure дозволяє легко (і весело!) Писати у функціональному стилі з незмінними структурами даних (без змін стану). Це масово зменшує потребу в налагоджувачі. Коли я знаю, що всі компоненти поводяться так, як я очікую (звертаючи особливу увагу на типи речей), то поведінка у великих масштабах рідко є проблемою.
Якщо ви використовуєте emacs / slime / swank, спробуйте це у відповіді:
(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, яка працює в поєднанні з вашим repl, а також консоллю вашого браузера. Ви можете розпорошувати dbg
(налагоджувати) або clog
(console.log) макроси у своєму коді та легко спостерігати за результатами окремих функцій тощо, надрукованими у вашій REPL та / або консолі.
З проекту Readme :
Основне використання
Це простий приклад. Макрос dbg друкує оригінальну форму і симпатично друкує оцінене значення у вікні REPL. Потім він повертає значення, не втручаючись у виконання коду.
Якщо ви обгорніть код таким dbg, як це,
(* 2 (dbg (+ 10 20))) ; => 60
наступне буде надруковано у вікні REPL.
Вихід REPL:
dbg: (+ 10 20) => 30
Вкладений dbg
Макрос dbg можна вкласти.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
Вихід REPL:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Уго Дункан та його співробітники продовжують робити дивовижну роботу над проектом ritz . Ritz-nrepl - сервер nREPL з можливостями налагодження. Перегляньте налагоджувачі Hugo у Clojure, що розмовляють на Clojure / Conj 2012, щоб побачити це в дії, на відео деякі слайди не читаються, тому ви можете побачити слайди звідси .
Використовуйте spyscope, який реалізує макрос спеціального зчитувача, щоб ваш код налагодження також був виробничим кодом https://github.com/dgrnbrg/spyscope
Починаючи з Java та ознайомлюючись із Eclipse, мені подобається, що може запропонувати проти годинникової стрілки (плагін 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 у ряд defs. Деяка заслуга йде тут
(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)])