Налагодження в Clojure? [зачинено]


227

Які найкращі способи налагодити код Clojure, використовуючи repl?


Відповіді:


158

Існує також 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

2
Приємно, але як вам зробити clojure, щоб знайти 'clojure.contrib.trace? У мене на моєму уроці є баночка із клоурі-внесками, але REPL каже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)
LarsH

2
Чи можете ви неправильно написати clojure як закриття, чи це помилка в коментарі? Чи можете ви завантажити інші бібліотеки clojure.contrib?
Джон Лоуренс Аспден

12
Станом на 1.3, це перемістилося на clojure.tools.trace ( github.com/clojure/tools.trace )
Джордж

4
Якщо ви отримуєте: "IllegalStateException не може динамічно прив'язувати нединамічний var" дивіться тут: stackoverflow.com/questions/8875353/…
Корнелій

2
Чи працює він і у версії 1.5? Я вивчаю Clojure за допомогою clojure koans, але ще не можу дотриматись роботи.
Nha

100

У мене є невеликий макрос налагодження, який мені здається дуже корисним:

;;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.
Заз

4
Ще краще: Спіскоп .
Заз

@Zaz Я повністю згоден. Спіскоп - приголомшливий! Можливо, навіть краще, ніж налагоджувач. Звичайно для набору тексту.
Дж. Аткін

66

CIDER Emacs отримав відладчик джерела, що ви можете поетапно виражатись за допомогою вираження всередині буфера Emacs і навіть вводити нові значення. Про це ви можете прочитати тут . Демонстраційний знімок екрана:

Налагодження CIDER


46

Мій улюблений метод - це ліберальне розповсюдження printlns по всьому коду ... Увімкнення та вимкнення їх легко завдяки #_макросу читача (який змушує читача читати у наступній формі, а потім робити вигляд, що його ніколи не бачили). Або ви можете використовувати макрос, що розширюється або на тіло, що передається, або 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)
                   ^^^

5
Насправді є версія debug-repl, яка працює з swank зараз: hugoduncan.org/post/2010/… (сповіщення спойлера: це приголомшливо)

1
Правильно, і тут добре мати посилання, дякую! Домовились про дивовижне. :-)
Міхал Марчик

Якщо це ваш стиль, вам може сподобатися бібліотека налагодження, згадана в наступній відповіді. github.com/philoskim/debux
Маллорі-Ерік

@ Mallory-Erik Спасибі, я перевірю це!
Міхал Марчик

37

Ви також можете вставити код, щоб потрапити до 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.


16

"кращі способи налагодження коду Clojure, використовуючи repl"

Трохи ліве поле, але "використовуючи REPL-сам".

Я пишу про любителя Clojure більше року і не відчував великої потреби в будь-яких інструментах налагодження. Якщо ви зберігаєте свої функції невеликими та запускаєте кожну із очікуваними входами на REPL та спостерігаєте за результатами, тоді має бути можливість мати досить чітке уявлення про те, як поводиться ваш код.

Я вважаю, що налагоджувач є найбільш корисним для спостереження за STATE у запущеній програмі. Clojure дозволяє легко (і весело!) Писати у функціональному стилі з незмінними структурами даних (без змін стану). Це масово зменшує потребу в налагоджувачі. Коли я знаю, що всі компоненти поводяться так, як я очікую (звертаючи особливу увагу на типи речей), то поведінка у великих масштабах рідко є проблемою.


Це здебільшого вірно, але, наприклад, коли у вас є рекурсія на декількох функціях, це не так просто.
Іван

9

Якщо ви використовуєте 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

як було сказано в коментарі вище.


9

Для IntelliJ є чудовий плагін Clojure під назвою Cursive . Крім усього іншого, він пропонує REPL, який можна запустити в режимі налагодження і переглядати ваш код Clojure так само, як, наприклад, Java.

Я би другий відповідь Пітера Вестмакотта, хоча, на моєму досвіді, саме запуск частин мого коду в REPL є більшою частиною часу достатньою формою налагодження.


Я використовував La Clojure з успіхом, але, схоже, вмирає на користь скоропису зараз github.com/JetBrains/la-clojure/blob/master/README.md
leeor

Але як налагоджувати Leiningen, показує:Error running 'ring server': Trampoline must be enabled for debugging
Gank

Це, здається, є специфічним ringабо lein- можливо, варто поставити окреме запитання?
dskrvk

6

З 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


5

Уго Дункан та його співробітники продовжують робити дивовижну роботу над проектом ritz . Ritz-nrepl - сервер nREPL з можливостями налагодження. Перегляньте налагоджувачі Hugo у Clojure, що розмовляють на Clojure / Conj 2012, щоб побачити це в дії, на відео деякі слайди не читаються, тому ви можете побачити слайди звідси .




1

Ось приємний макрос для налагодження складних 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)))

... і есе, що пояснює його використання .


-4

Версія функції 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)])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.