在使用repl时,调试Clojure代码的最佳方法是什么?
还有一个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)))))
那么 Bob 再次成为你的叔叔:
(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)
- LarsH我有一个小的调试宏,我觉得非常有用:
;;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)
我最喜欢的方法是在代码中大量使用println
... 通过使用#_
读取宏(将阅读器读入以下形式,然后假装从未看到),轻松地打开和关闭它们。或者您可以使用一个宏,将其扩展为传入的主体或者nil
,具体取决于某些特殊变量的值,比如说*debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
如果将 (def *debug* false)
改为真值 true
,它将扩展为包裹在 do
中的 body
,否则将扩展为 nil
。这个SO问题的被接受答案 在调试序列操作时非常有用。
此外,还有一些与 swank-clojure REPL 不兼容但是值得一提的工具:debug-repl
。您可以在独立的REPL中使用它(例如使用 Leiningen (lein repl
)很容易获得),如果从命令行启动程序,则会在终端中自动打开它自己的REPL。其思想是:您可以随时在任何地方加入 debug-repl
宏,并在程序执行到达该点时带出它自己的REPL,包括所有本地变量等。几个相关链接:Clojure debug-repl,Clojure debug-repl tricks, how 'bout a debug-repl (on the Clojure Google group), debug-repl on Clojars。
当使用 swank-clojure 与 Clojure 代码一起工作时,它可以很好地利用SLIME内置的调试器,注意到无关的堆栈跟踪部分被灰色处理,因此可以轻松找到实际的问题所在。要记住的一件事是,没有“名称标签”的匿名函数在堆栈跟踪中出现时几乎没有有用的信息附加在其上;当添加“名称标签”时,它会出现在堆栈跟踪中,一切又恢复正常了:
(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 [a b c]
(let [d (some-calc)]
(debug-repl)))
我将这个代码放进了我的user.clj文件中,这样在所有的REPL会话中都可以使用它。
“使用repl调试Clojure代码的最佳方法”
略微有些意外,但是“使用repl本身”。
我已经写了一年余业余爱好者的Clojure,没有感觉到需要任何调试工具。如果您将函数保持小巧,并在REPL上用预期的输入运行每个函数并观察结果,那么应该能够对代码运行情况有一个相当清晰的认识。
我发现调试器在观察正在运行的应用程序的状态时最有用。Clojure使得以不可变数据结构(不改变状态)的方式编写变得容易(而且有趣!)。这大大减少了使用调试器的需求。一旦我知道所有组件的运作方式符合我的期望(特别关注事物的类型),那么大规模的行为很少会出现问题。
(defn factorial [n]
(cond (< n 2) n
(= n 23) (swank.core/break)
:else (* n (factorial (dec n)))))
(factorial 30)
这是以下作者的杰作:
http://hugoduncan.org/post/2010/swank_clojure_gets_a_break_with_the_local_environment.xhtml
正如上面的评论中提到的那样。
对于IntelliJ,有一个名为Cursive的优秀Clojure插件。除其他外,它提供了一个REPL,您可以在调试模式下运行并像Java一样逐步执行Clojure代码。
不过,根据我的经验,仅在REPL中运行代码片段大多数情况下已足以进行调试。
Leiningen
进行调试,它显示:Error running 'ring server': Trampoline must be enabled for debugging
。 - Gankring
或 lein
- 或许值得发布一个单独的问题? - dskrvk截至2016年,您可以使用Debux,这是一个简单的调试库,适用于Clojure/Script,可与您的repl以及浏览器控制台一起使用。您可以在代码中使用dbg
(debug)或clog
(console.log)宏,并轻松观察打印到您的REPL和/或控制台的各个函数的结果等。
从该项目的Readme中:
Basic usage
This is a simple example. The macro dbg prints an original form and pretty-prints the evaluated value on the REPL window. Then it returns the value without interfering with the code execution.
If you wrap the code with dbg like this,
(* 2 (dbg (+ 10 20))) ; => 60
the following will be printed in the REPL window.
REPL output:
dbg: (+ 10 20) => 30
Nested dbg
The dbg macro can be nested.
(dbg (* 2 (dbg (+ 10 20)))) ; => 60
REPL output:
`dbg: (+ 10 20) => 30`
dbg: (* 2 (dbg (+ 10 20))) => 60
Hugo Duncan和他的合作者们在ritz项目中继续做出惊人的工作。 Ritz-nrepl是一个带有调试功能的nREPL服务器。观看Hugo在Clojure/Conj 2012上的Debuggers in Clojure演讲,您可以在其中看到它的实际运行情况。由于视频中的一些幻灯片无法阅读,所以您可能需要从此处查看幻灯片。