Odpowiedzi:
Istnieje również dotrace, który pozwala spojrzeć na wejścia i wyjścia wybranych funkcji.
(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(dotrace [fib] (fib 3))
daje wynik:
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
W Clojure 1.4 dotrace
przeprowadził się:
Potrzebujesz zależności:
[org.clojure/tools.trace "0.7.9"]
(require 'clojure.tools.trace)
I musisz dodać dynamikę ^: do definicji funkcji
(defn ^:dynamic fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
Zatem Bob po raz kolejny jest wujem:
(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)
Mam małe makro debugujące, które uważam za bardzo przydatne:
;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
Możesz wstawić go tam, gdzie chcesz oglądać, co się dzieje i kiedy:
;; 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
.
Moją ulubioną metodą jest swobodne posypywanie println
s całego kodu ... Włączanie i wyłączanie ich jest łatwe dzięki #_
makro czytelnika (które sprawia, że czytnik czyta w następującej formie, a potem udaje, że go nigdy nie widział). Możesz też użyć makra rozwijającego się do przekazywanej treści lub w nil
zależności od wartości jakiejś specjalnej zmiennej, powiedz *debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
Z (def *debug* false)
tam, to rozwinie się do nil
. Z true
, rozwinie się do body
owiniętego w do
.
Przyjęta odpowiedź na to pytanie SO: Idiomatic Clojure do zgłaszania postępów? jest bardzo pomocny podczas debugowania operacji sekwencji.
Potem jest coś, co jest obecnie niezgodne z Swank-Clojure „s REPL, ale jest zbyt dobre, nie wspominając: debug-repl
. Możesz go używać w samodzielnej REPL, którą łatwo uzyskać np. Za pomocą Leiningen ( lein repl
); a jeśli uruchamiasz swój program z wiersza poleceń, to uruchomi on swój REPL bezpośrednio w twoim terminalu. Chodzi o to, że można upuścić debug-repl
makra w dowolnym miejscu i mieć go wychować własne rEPL gdy osiągnie wykonawcze zawarte w programie, które wskazują, ze wszystkich mieszkańców w zakresie itd. Kilka istotnych linków: Clojure debug-rEPL , Clojure debug -repl sztuczki , jak o debug-repl (w grupie Clojure Google), debug-repl w Clojars .
swank-clojure wykonuje odpowiednią pracę, czyniąc wbudowany debugger SLIME użytecznym podczas pracy z kodem Clojure - zwróć uwagę na to, jak niepotrzebne bity stacktrace są wyszarzone, więc łatwo jest znaleźć rzeczywisty problem w debugowanym kodzie. Należy pamiętać, że anonimowe funkcje bez „znaczników nazw” pojawiają się w stacktrace, w zasadzie bez żadnych przydatnych informacji; po dodaniu „plakietki” pojawia się w pliku śledzenia i wszystko jest w porządku:
(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)
^^^
Możesz także wstawić kod, aby przejść do REPL ze wszystkimi lokalnymi powiązaniami, używając Alexa Osborne'adebug-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))))
Następnie, aby go użyć, wstaw go tam, gdzie chcesz rozpocząć replikę:
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
Wrzucam to do pliku user.clj, aby był dostępny we wszystkich sesjach REPL.
„najlepsze sposoby debugowania kodu Clojure podczas korzystania z repl”
Nieznacznie lewe pole, ale „sam korzystam z REPL”.
Od ponad roku piszę hobbystkę Clojure i nie odczuwałem potrzeby stosowania żadnych narzędzi do debugowania. Jeśli utrzymujesz swoje funkcje na małym poziomie i uruchamiasz każdą z oczekiwanymi danymi wejściowymi na REPL i obserwujesz wyniki, powinien istnieć całkiem jasny obraz tego, jak zachowuje się twój kod.
Uważam, że debuger jest najbardziej przydatny do obserwowania stanu w uruchomionej aplikacji. Clojure ułatwia (i sprawia przyjemność!) Pisanie w funkcjonalnym stylu z niezmiennymi strukturami danych (bez zmieniającego się stanu). To znacznie zmniejsza potrzebę debuggera. Kiedy już wiem, że wszystkie komponenty zachowują się tak, jak się spodziewam (zwracając szczególną uwagę na rodzaje rzeczy), zachowanie na dużą skalę rzadko stanowi problem.
Jeśli używasz emacs / slime / swank, spróbuj tego na REPL:
(defn factorial [n]
(cond (< n 2) n
(= n 23) (swank.core/break)
:else (* n (factorial (dec n)))))
(factorial 30)
Nie daje ci śladu pełnego stosu, jak w LISP-ie, ale jest dobry do przeszukiwania.
To jest dobra praca:
http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml
jak wspomniano w komentarzu powyżej.
Dla IntelliJ istnieje doskonała wtyczka Clojure o nazwie Cursive . Zapewnia między innymi REPL, który można uruchomić w trybie debugowania i przechodzić przez kod Clojure, tak jak na przykład w Javie.
Poparłbym odpowiedź Petera Westmacotta, chociaż z mojego doświadczenia wynika, że po prostu uruchamianie fragmentów mojego kodu w REPL jest przez większość czasu wystarczającą formą debugowania.
Leiningen
, pokazuje:Error running 'ring server': Trampoline must be enabled for debugging
ring
lub lein
- może warto zadać osobne pytanie?
Od 2016 roku możesz używać Debux , prostej biblioteki debugowania dla Clojure / Script, która działa w połączeniu z twoją repliką, a także konsolą przeglądarki. Możesz posypywać dbg
(debugować) lub clog
(console.log) makra w swoim kodzie i łatwo obserwować wyniki poszczególnych funkcji itp. Wydrukowane na REPL i / lub konsoli.
Z pliku Readme projektu :
Podstawowe użycie
To jest prosty przykład. Makro dbg drukuje oryginalny formularz i ładnie drukuje oszacowaną wartość w oknie REPL. Następnie zwraca wartość bez zakłócania wykonywania kodu.
Jeśli otoczysz kod dbg w ten sposób,
(* 2 (dbg (+ 10 20))) ; => 60
następujące elementy zostaną wydrukowane w oknie REPL.
Wyjście REPL:
dbg: (+ 10 20) => 30
Zagnieżdżona dbg
Makro dbg można zagnieżdżać.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
Wyjście REPL:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Hugo Duncan i współpracownicy nadal wykonują niesamowitą pracę z projektem Ritz . Ritz-nrepl to serwer nREPL z funkcjami debugowania. Obejrzyj debugowanie Hugo w debiucie w Clojure na Clojure / Conj 2012, aby zobaczyć, jak działa, w filmie niektóre slajdy nie są czytelne, więc możesz chcieć je obejrzeć tutaj .
Użyj spyscope, który implementuje niestandardowe makro czytnika, aby kod debugowania był również kodem produkcyjnym https://github.com/dgrnbrg/spyscope
Pochodząc z Javy i znając Eclipse, podoba mi się to, co ma do zaoferowania Counterclockwise (wtyczka Eclipse do programowania Clojure): http://doc.ccw-ide.org/documentation.html#_debug_clojure_code
Oto ładne makro do debugowania skomplikowanych let
formularzy:
(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)))
... i esej wyjaśniający jego użycie .
Wersja funkcji def-let, która zmienia let w serię def. Trochę tutaj zasługuje
(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)))))
Zastosowanie: Wymaga cytowania treści z cytatem, np
(def-let '[a 1 b 2 c (atom 0)])