Answers:
还有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
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)
我有一个小的调试宏,我发现它非常有用:
;;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
在代码中自由散布s… 由于有reader宏(很容易#_
使读者以以下形式阅读,然后假装从未见过),因此打开和关闭它们很容易。或者,您可以使用宏扩展为传入的主体,也可以nil
根据某些特殊变量的值进行扩展,例如*debug*
:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
随着(def *debug* false)
在那里,这将扩大到nil
。使用时true
,它会扩展到body
包裹在一个do
。
这样的SO问题的公认答案:用于进度报告的惯用Clojure?在调试序列操作时非常有用。
然后有一些当前与swank-clojure的REPL 不兼容的东西,但太好了,更不用说了:debug-repl
。您可以在独立的REPL中使用它,例如通过Leiningen(lein repl
)可以轻松获得它。如果您要从命令行启动程序,那么它将在您的终端中直接启动自己的REPL。这个想法是,您可以将debug-repl
宏放在任意位置,并在程序执行到该位置时使其具有自己的REPL,所有本地变量都在作用域中。几个相关链接:Clojure debug-repl,Clojure debug -repl技巧,如何在Clojars上调试(在Clojure Google组中)调试-repl。
swank-clojure可以很好地使SLIME的内置调试器在使用Clojure代码时发挥作用-请注意,stacktrace的无关位是如何变灰的,因此很容易在被调试的代码中找到实际的问题。要记住的一件事是,没有“名称标签”的匿名函数出现在堆栈跟踪中,基本上没有附加有用的信息;当添加“名称标签”时,它确实出现在堆栈跟踪中,并且一切都很好:
(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上使用预期的输入运行每个函数并观察结果,那么应该可以很清楚地了解代码的行为方式。
我发现调试器对于观察正在运行的应用程序中的STATE最有用。Clojure使使用具有不变数据结构(无变化状态)的功能样式编写起来容易(有趣)!这大大减少了对调试器的需求。一旦我知道所有组件的行为都符合我的预期(特别注意事物的类型),那么大规模行为就几乎不会成为问题。
如果您使用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,有一个出色的Clojure插件称为Cursive。除其他外,它提供了REPL,您可以在调试模式下运行该REPL,并像在Java中一样逐步浏览Clojure代码。
尽管我认为彼得·韦斯特马科特(Peter Westmacott)的回答是第二位的,但根据我的经验,在大多数情况下,仅在REPL中运行我的代码片段是一种足够的调试形式。
Leiningen
,它显示:Error running 'ring server': Trampoline must be enabled for debugging
ring
或lein
-也许值得提出一个单独的问题?
从2016年开始,您可以使用Debux,这是Clojure / Script的简单调试库,可与您的repl以及浏览器的控制台配合使用。您可以在代码中添加dbg
(调试)或clog
(console.log)宏,并轻松观察打印到REPL和/或控制台的各个功能等的结果。
从项目的自述文件中:
基本用法
这是一个简单的例子。宏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
雨果·邓肯(Hugo Duncan)和合作者继续在Ritz项目中完成出色的工作。Ritz-nrepl是具有调试功能的nREPL服务器。在Clojure / Conj 2012上观看Hugo 在Clojure中的Debuggers演讲,看看它的实际效果,在视频中一些幻灯片不可读,因此您可能希望从此处查看幻灯片。
使用实现自定义阅读器宏的spyscope,以便您的调试代码也是生产代码 https://github.com/dgrnbrg/spyscope
来自Java并熟悉Eclipse,我喜欢逆时针(Clojure开发的Eclipse插件)所提供的功能:http : //doc.ccw-ide.org/documentation.html#_debug_clojure_code
这是一个调试复杂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)))
...以及一篇说明其用途的文章。
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)])