Отладка в Clojure? [закрыто]


227

Каковы лучшие способы отладки кода Clojure при использовании repl?


В дополнение к ответам ниже см. «Инструменты и методы отладки» в руководстве по REPL: clojure.org/guides/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)

И вам нужно добавить ^: dynamic в определение функции

(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? На моем пути к 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)
классу

2
Может быть, вы ошибочно написали clojure как закрытие или это опечатка в комментарии? Можете ли вы загрузить другие библиотеки clojure.contrib?
Джон Лоуренс Аспден

12
Начиная с версии 1.3 это переместилось в clojure.tools.trace ( github.com/clojure/tools.trace )
Джордж,

4
Если вы получаете: «IllegalStateException не может динамически связывать нединамическую переменную», смотрите здесь: stackoverflow.com/questions/8875353/…
Cornelius

2
Это работает в версии 1.5 также? Я изучаю Clojure с помощью clojure koans, но пока не могу заставить работать dotrace.
Ня,

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
Еще лучше: Spyscope .
Заз

@Zaz, я полностью согласен. Spyscope потрясающий! Возможно, даже лучше, чем отладчик. Конечно, для набора текста.
Дж Аткин

66

CIDER в Emacs получил отладчик исходного кода, который позволяет пошагово выражать выражения по выражению в буфере Emacs и даже вводить новые значения. Вы можете прочитать все об этом здесь . Демо скриншот:

CIDER отладка


46

Мой любимый метод - либеральное разбрасывание printlns по всему коду ... Включать и выключать их легко благодаря #_макросу читателя (который заставляет читателя читать в следующем виде, а затем притворяться, что его никогда не видели). Или вы можете использовать макрос, расширяющийся либо до переданного тела, либо в nilзависимости от значения некоторой специальной переменной, скажем *debug*:

(defmacro debug-do [& body]
  (when *debug*
    `(do ~@body)))

С (def *debug* false)там, это расширится до nil. Причём true, он расширится, чтобы bodyзавернуться в do.


Принятый ответ на этот SO вопрос: идиоматическая Clojure для отчетов о прогрессе? очень полезно при отладке последовательностей операций.


Тогда есть то , что в настоящее время несовместимо с шикарным-Clojure «s REPL, но слишком хорошо , чтобы не упомянуть: debug-repl. Вы можете использовать его в автономном REPL, который легко получить, например, с помощью Leiningen ( lein repl); и если вы запускаете вашу программу из командной строки, то она выведет свой собственный REPL прямо в вашем терминале. Идея состоит в том, что вы можете debug-replоставить макрос в любом месте и вызвать его собственный REPL, когда выполнение программы достигнет этой точки, со всеми локальными объектами в области видимости и т. Д. Несколько соответствующих ссылок: Clojure debug-repl , Clojure debug -relp трюки , как насчет debug-repl (в группе Google Clojure), 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
Хорошо, и хорошо, что есть ссылка здесь, спасибо! Договорились офигенно. :-)
Михал Марчик

Если это ваш стиль, вам может понравиться библиотека debux, упомянутая в последующем ответе. github.com/philoskim/debux
Мэллори-Эрик

@ Мэллори-Эрик Спасибо, я проверю это!
Михал Марчик

37

Вы также можете вставить код, чтобы добавить себя в REPL со всеми локальными привязками, используя Alex Osbornedebug-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))))

Затем, чтобы использовать его, вставьте его туда, где вы хотите, чтобы repl запускался:

(defn my-function [a b c]
  (let [d (some-calc)]
    (debug-repl)))

Я вставляю это в мой user.clj, чтобы он был доступен во всех сеансах REPL.


16

«Лучшие способы отладки кода Clojure при использовании repl»

Слегка влево, но «используя сам REPL».

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

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


Это в основном верно, но когда у вас есть рекурсия по нескольким функциям, например, это не так просто.
Джон

9

Если вы используете emacs / slime / swank, попробуйте это в REPL:

(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, которая работает как с вашим реплеем, так и с консолью вашего браузера. Вы можете посыпать dbg(отладить) или clog(console.log) макросы в своем коде и легко наблюдать результаты отдельных функций и т. Д., Напечатанные на ваш REPL и / или консоль.

Из файла Readme проекта :

Основное использование

Это простой пример. Макрос dbg печатает оригинальную форму и довольно печатает оцененное значение в окне REPL. Затем он возвращает значение, не мешая выполнению кода.

Если вы оберните код с dbg, как это,

(* 2 (dbg (+ 10 20))) ; => 60

следующее будет напечатано в окне REPL.

REPL выход:

dbg: (+ 10 20) => 30

Вложенная база данных

Макрос dbg может быть вложенным.

(dbg (* 2 (dbg (+ 10 20)))) ; => 60

REPL выход:

`dbg: (+ 10 20) => 30`  

dbg: (* 2 (dbg (+ 10 20))) => 60


5

Хьюго Дункан и его коллеги продолжают удивительную работу с проектом Ritz . Ritz-nrepl - это сервер nREPL с возможностями отладки. Посмотрите выступление Хьюго « Отладчики в 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 в серию определений. Некоторый кредит идет сюда

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