在Clojure中debugging?

debuggingClojure代码的最佳方式是什么,而使用repl?

还有dotrace,它允许您查看所选函数的input和输出。

(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 

我有一点debuggingmacros,我觉得非常有用:

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

Emacs的CIDER有一个源代码debugging器,您可以通过Emacs缓冲区内的expression式来执行expression式,甚至可以注入新的值。 你可以在这里阅读。 一个演示截图:

CIDER调试

我最喜欢的方法是遍布代码的自由散布的println打开和closures它很容易感谢#_阅读器macros(这使读者阅读以下forms,然后假装从来没有见过它)。 或者,你可以使用一个macros扩展到一个传入的正文或nil取决于一些特殊的variables的值,说*debug*

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

在那里有一个(def *debug* false) ,这将会扩展到niltrue ,它会扩大到包裹在一个body


对这个SO问题的接受答案是: Clojure习惯性的进展报告? 在debugging序列操作时非常有用。


然后有一些目前与swank-clojure的REPL 不兼容 ,但不用说: debug-repl 。 你可以在独立的REPL中使用它,这很容易得到,例如Leiningen( lein repl ); 如果你从命令行启动你的程序,那么它会把你自己的REPL放在你的terminal上。 这个想法是,你可以在你喜欢的任何地方放置debug-replmacros,并在程序执行到达时启动它自己的REPL,所有的本地人都在范围之内等几个相关的链接: Clojure debug-repl , Clojure debug-repl技巧 , 如何在Clojure上debuggingrepl (在Clojure Google小组上), 在Clojars上debuggingrepl 。


在使用Clojure代码时,swank-clojure做了一个足够的工作,使得SLIME的内置debugging器很有用 – 请注意stacktrace的不相关位是灰色的,这样很容易在被debugging的代码中find实际的问题。 有一件事要记住,没有“名称标签”的匿名函数会出现在堆栈跟踪中,基本上没有任何有用的信息。 当一个“名称标签”被添加,它确实出现在堆栈跟踪,一切都很好:

 (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) ^^^ 

你也可以使用Alex Osborne的debug-repl来插入代码,把自己放到一个带有所有本地绑定的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 [abc] (let [d (some-calc)] (debug-repl))) 

我坚持这个在我的user.clj,所以它可以在所有的REPL会话。

“debuggingClojure代码的最佳方式,同时使用repl”

略微左移,但是“使用REPL iteself”。

我一直在写业余爱好者Clojure一年多,并没有任何debugging工具的需要。 如果你保持你的函数很小,并在REPL中运行每一个预期的input,并观察结果,那么应该可以清楚地了解代码的行为。

我发现一个debugging器对于观察正在运行的应用程序中的STATE是非常有用的。 Clojure使得用不可变的数据结构(不改变状态)写入一个函数式风格变得简单(而且很有趣)。 这大大减less了对debugging器的需求。 一旦我知道所有组件的行为都像我所期望的那样(特别注意事物的types),那么大规模的行为就不是什么问题。

如果你使用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

正如上面评论中提到的那样。

对于IntelliJ,有一个名为Cursive的优秀Clojure插件。 除此之外,它提供了一个REPL,你可以在debugging模式下运行,并像在Java中那样遍历Clojure代码。

我会第二个Peter Westmacott的答案,虽然在我的经验,只是在REPL中运行我的代码段大部分时间是足够的debuggingforms。

雨果·邓肯(Hugo Duncan)和合作者继续在丽思计划中做出令人惊叹的工作。 Ritz-nrepl是一个具有debuggingfunction的nREPL服务器。 观看Clojure / Conj 2012上的Clojure谈话中的Hugo debugging器,看看它在行动,在video中的一些幻灯片是不可读的,所以你可能想从这里查看幻灯片。

截至2016年,您可以使用Debux ,一个简单的Clojure / Scriptdebugging库,与您的repl以及您的浏览器控制台一起使用。 您可以在代码中使用dbg (debug)或clog (console.log)macros,并轻松观察打印到REPL和/或控制台的各个函数等的结果。

从项目的自述文件 :

基本用法

这是一个简单的例子。 macrosdbg打印原始表单,并在REPL窗口上打印评估值。 然后它返回值而不会干扰代码执行。

如果你用这样的dbg包装代码,

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

将在REPL窗口中打印以下内容。

REPL输出:

dbg: (+ 10 20) => 30

嵌套的dbg

dbgmacros可以嵌套。

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

REPL输出:

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

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

使用实现自定义阅读器macros的spyscope,以便您的debugging代码也是生产代码https://github.com/dgrnbrg/spyscope

这里有一个很好的macros来debugging复杂的letforms:

 (defmacro def+ "def with binding (def+ [{:keys [abd]} {: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))) 

…和一篇文章解释它的使用 。

来自Java并熟悉Eclipse,我喜欢逆时针(Clojure开发的Eclipse插件)所提供的: http : //doc.ccw-ide.org/documentation.html#_debug_clojure_code

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