Yanıtlar:
Ayrıca seçilen fonksiyonların giriş ve çıkışlarına bakmanızı sağlayan dotrace de vardır.
(use 'clojure.contrib.trace)
(defn fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
(dotrace [fib] (fib 3))
çıktı üretir:
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'te dotrace
taşındı:
Bağımlılığa ihtiyacınız var:
[org.clojure/tools.trace "0.7.9"]
(require 'clojure.tools.trace)
Ve işlev tanımına ^: dinamik eklemeniz gerekir
(defn ^:dynamic fib[n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
Sonra Bob bir kez daha amcan:
(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)
Çok yararlı buldum küçük bir hata ayıklama makro var:
;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
Neler olup bittiğini izlemek istediğiniz yere ekleyebilirsiniz:
;; 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
.
En sevdiğim yöntem, println
kodun her tarafına liberal bir serpiştirmektir ... Okuyucu makrosu sayesinde bunları açmak ve kapatmak kolaydır#_
(bu, okuyucuyu aşağıdaki formda okur, daha sonra hiç görmediğini varsayar). Ya da, geçiş yapılan bir gövdeye genişleyen veya nil
bazı özel değişkenlerin değerine bağlı olarak bir makro kullanabilirsiniz *debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
Bir ile (def *debug* false)
orada, bu genişleyecek nil
. İle true
, body
a do
.
Bu SO sorusunun kabul edilen cevabı: İlerleme raporlaması için Deyimsel Clojure? dizi işlemlerinde hata ayıklama yaparken çok faydalıdır.
Sonra bir şey var Swank-Clojure ile henüz uyumlu 'ın REPL ancak söz kadar iyi değil: debug-repl
. Bunu, örneğin Leiningen ( lein repl
) ile elde edilmesi kolay olan bağımsız bir REPL'de kullanabilirsiniz ; ve programınızı komut satırından başlatıyorsanız, kendi REPL'ini doğrudan terminalinize getirecektir. Fikir, debug-repl
makroyu istediğiniz yere bırakabilir ve programın yürütülmesi bu noktaya ulaştığında, tüm yerel halkın kapsamı vb. İle kendi REPL'ini getirmesini sağlayabilirsiniz. İlgili birkaç bağlantı: Clojure hata ayıklama , Clojure hata ayıklama -repl hileler , nasıl bir hata ayıklama-repl (Clojure Google grubunda), Clojars üzerinde hata ayıklama-repl hakkında .
swank-clojure, Clojure koduyla çalışırken SLIME'nin yerleşik hata ayıklayıcısını kullanışlı hale getirmek için yeterli bir iş yapar - yığın izinin alakasız bitlerinin nasıl gri renkte olduğunu not edin, böylece hata ayıklanan koddaki asıl sorunu bulmak kolaydır. Akılda tutulması gereken bir şey, "ad etiketleri" olmayan anonim işlevlerin, temelde kendilerine hiçbir yararlı bilgi eklenmeden yığın izinde görünmesidir; bir "ad etiketi" eklendiğinde, yığın izinde görünür ve her şey yolundadır:
(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)
^^^
Ayrıca Alex Osborne'larıdebug-repl
kullanarak kendinizi tüm yerel bağlantılarla bir REPL'e bırakmak için kod ekleyebilirsiniz :
(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))))
Ardından kullanmak için, repl'in başlamasını istediğiniz yere yerleştirin:
(defn my-function [a b c]
(let [d (some-calc)]
(debug-repl)))
Bunu tüm REPL oturumlarında kullanılabilmesi için user.clj dosyasına yapıştırıyorum.
"Repl kullanırken Clojure kodunda hata ayıklamanın en iyi yolları"
Biraz sol alan, ancak 'REPL'i kullanmak'.
Bir yılı aşkın süredir hobist Clojure yazıyorum ve herhangi bir hata ayıklama aracına büyük ihtiyaç duymadım. İşlevlerinizi küçük tutarsanız ve her birini REPL'de beklenen girişlerle çalıştırırsanız ve sonuçları gözlemlerseniz, kodunuzun nasıl davrandığına dair oldukça net bir resim elde etmek mümkün olmalıdır.
Bir hata ayıklayıcı çalışan bir uygulamada STATE gözlemlemek için en yararlı buluyorum. Clojure, değiştirilemez veri yapıları (değişen durum yok) ile işlevsel bir tarzda yazmayı kolaylaştırır (ve eğlenceli!). Bu hata ayıklayıcı ihtiyacını büyük ölçüde azaltır. Tüm bileşenlerin beklediğim gibi davrandığını bildiğimde (şey türlerine özellikle dikkat ederek), büyük ölçekli davranış nadiren bir problemdir.
Emacs / slime / swank kullanıyorsanız, bunu REPL'de deneyin:
(defn factorial [n]
(cond (< n 2) n
(= n 23) (swank.core/break)
:else (* n (factorial (dec n)))))
(factorial 30)
LISP altına girdiğiniz gibi size tam bir yığın izlemesi vermez, ancak etrafa atılmak için iyidir.
Bu iyi bir iş:
http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml
yukarıdaki yorumda belirtildiği gibi.
IntelliJ için Cursive adında mükemmel bir Clojure eklentisi var . Diğer şeylerin yanı sıra, hata ayıklama modunda çalıştırabileceğiniz ve tıpkı Java için olduğu gibi Clojure kodunuzda adım atabileceğiniz bir REPL sağlar.
Peter Westmacott'un cevabını ikinci olarak verdim, ancak tecrübelerime göre REPL'deki kodumun parçalarını çalıştırmak çoğu zaman yeterli bir hata ayıklama biçimidir.
Leiningen
Error running 'ring server': Trampoline must be enabled for debugging
ring
veya lein
belki de ayrı bir soru göndermeye değer gibi görünüyor ?
2016 itibarıyla Debux'u kullanabilirsiniz . , tarayıcınızın yanı sıra tarayıcınızın konsolu ile birlikte çalışan Clojure / Script için basit bir hata ayıklama kütüphanesi olan . Kodunuza makro serpiştirebilir dbg
(hata ayıklayabilir) veya clog
(console.log) ve REPL ve / veya konsolunuza yazdırılan bireysel işlevlerin vb. Sonuçlarını kolayca gözlemleyebilirsiniz.
Projenin Benioku dosyasından :
Temel kullanım
Bu basit bir örnek. Dbg makrosu orijinal bir form yazdırır ve REPL penceresinde değerlendirilen değeri güzel yazdırır. Sonra kod yürütmesine müdahale etmeden değeri döndürür.
Kodu dbg ile bu şekilde sararsanız,
(* 2 (dbg (+ 10 20))) ; => 60
REPL penceresinde aşağıdakiler yazdırılacaktır.
REPL çıkışı:
dbg: (+ 10 20) => 30
İç içe dbg
Dbg makrosu yuvalanabilir.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
REPL çıkışı:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Hugo Duncan ve ortak çalışanlar, ritz projesi ile inanılmaz işler yapmaya devam ediyor . Ritz-nrepl , hata ayıklama özelliklerine sahip bir nREPL sunucusudur. Hugo'nun Clojure'daki Hata Ayıklayıcılarını Clojure / Conj 2012'deki konuşmasını izlemek için izleyin , videoda bazı slaytlar okunamıyor, bu yüzden slaytları buradan görüntülemek isteyebilirsiniz .
Hata ayıklama kodunuzun ayrıca üretim kodu olması için özel bir okuyucu makrosu uygulayan spyscope kullanın https://github.com/dgrnbrg/spyscope
Java'dan geliyor ve Eclipse'e aşina olduğum için, saat yönünün tersine (Clojure gelişimi için Eclipse eklentisi) neler sunduğunu seviyorum: http://doc.ccw-ide.org/documentation.html#_debug_clojure_code
İşte karmaşık hata ayıklama için güzel bir makrolet
formlarda :
(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)))
... ve kullanımını açıklayan bir deneme .
Bir izin dizisine dönüşen def-let işlevinin sürümü. Biraz kredi buraya gidiyor
(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)))))
Kullanım: İçeriği bir teklifle alıntılamanız gerekir, örn.
(def-let '[a 1 b 2 c (atom 0)])