在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式,甚至可以注入新的值。 你可以在这里阅读。 一个演示截图:
我最喜欢的方法是遍布代码的自由散布的println
… 打开和closures它很容易感谢#_
阅读器macros(这使读者阅读以下forms,然后假装从来没有见过它)。 或者,你可以使用一个macros扩展到一个传入的正文或nil
取决于一些特殊的variables的值,说*debug*
:
(defmacro debug-do [& body] (when *debug* `(do ~@body)))
在那里有一个(def *debug* false)
,这将会扩展到nil
。 true
,它会扩大到包裹在一个body
。
对这个SO问题的接受答案是: Clojure习惯性的进展报告? 在debugging序列操作时非常有用。
然后有一些目前与swank-clojure的REPL 不兼容 ,但不用说: debug-repl
。 你可以在独立的REPL中使用它,这很容易得到,例如Leiningen( lein repl
); 如果你从命令行启动你的程序,那么它会把你自己的REPL放在你的terminal上。 这个想法是,你可以在你喜欢的任何地方放置debug-repl
macros,并在程序执行到达时启动它自己的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复杂的let
forms:
(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)])