Clojure'da hata ayıklama? [kapalı]


227

Repl kullanırken Clojure kodunda hata ayıklamanın en iyi yolları nelerdir?


Aşağıdaki cevaplara ek olarak, REPL kılavuzundaki 'Hata ayıklama araçları ve teknikleri' konusuna bakın: clojure.org/guides/repl/…
Valentin Waeselynck

Yanıtlar:


158

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 dotracetaşı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

2
Güzel, ama 'clojure.contrib.trace' ı nasıl bulabilirsin? Sınıf 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)
yolumda

2
Clojure'u kapanış olarak yanlış yazabilir misiniz yoksa yorumdaki bir yazım hatası mı? Diğer clojure.contrib kütüphanelerini yükleyebilir misiniz?
John Lawrence Aspden

12
1.3 itibariyle bu clojure.tools.trace'a ( github.com/clojure/tools.trace ) taşındı
George

4
"IllegalStateException Dinamik olmayan değişkeni dinamik olarak bağlayamıyorum" ifadesini alıyorsanız, buraya bakın: stackoverflow.com/questions/8875353/…
Cornelius

2
1.5 sürümünde de çalışıyor mu? Clojure'u clojure koans'la öğreniyorum, ancak dotrace'in henüz çalışmasını sağlayamıyorum.
nha

100

Ç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)


4
Daha da iyisi: Spyscope .
Zaz

@Zaz Tamamen katılıyorum. Spyscope harika! Bir hata ayıklayıcıdan daha iyi olabilir. Kesinlikle yazmak için.
J Atkin

66

Emacs CIDER, bir Emacs arabelleğinin içindeki ifadeye göre ifadeyi adımlandırabileceğiniz ve hatta yeni değerler enjekte edebileceğiniz bir kaynak hata ayıklayıcısı aldı. Burada her şeyi okuyabilirsiniz . Demo ekran görüntüsü:

CIDER hata ayıklama


46

En sevdiğim yöntem, printlnkodun 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 nilbazı ö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, bodya 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-replmakroyu 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)
                   ^^^

5
Aslında şu anda swank ile çalışan bir hata ayıklama repl sürümü var: hugoduncan.org/post/2010/… (Spoiler uyarısı: harika)

1
Doğru, ve burada bir bağlantıya sahip olmak güzel, teşekkürler! Harika kabul etti. :-)
Michał Marczyk

Bu sizin tarzınızsa, sonraki yanıtta belirtilen debux kütüphanesini beğenebilirsiniz. github.com/philoskim/debux
Mallory-Erik

@ Mallory-Erik Teşekkürler, kontrol edeceğim!
Michał Marczyk

37

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.


16

"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.


Bu çoğunlukla doğrudur, ancak örneğin birden fazla işlevde özyinelemeniz olduğunda bu o kadar kolay değildir.
John


9

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.


La Clojure'u başarıyla kullanıyordum, ancak şimdi el yazısı lehine ölüyor gibi görünüyor github.com/JetBrains/la-clojure/blob/master/README.md
leeor

Ama nasıl hata LeiningenError running 'ring server': Trampoline must be enabled for debugging
ayıklanır

Bu ayrı bir soruya özgü ringveya leinbelki de ayrı bir soru göndermeye değer gibi görünüyor ?
dskrvk

6

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





1

İş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 .


-4

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)])
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.