当使用repl时,调试Clojure代码的最佳方法是什么?
当前回答
我有一个调试宏,我觉得非常有用:
;;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/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
正如上面的评论所提到的。
我最喜欢的方法是在整个代码中自由地散布printlns。多亏了#_ reader宏,打开和关闭它们很容易(它使阅读器以下面的形式阅读,然后假装从未见过它)。或者你可以使用宏扩展为传入体或nil,这取决于一些特殊变量的值,例如*debug*:
(defmacro debug-do [& body]
(when *debug*
`(do ~@body)))
这里有一个(def *debug* false),它将扩展为nil。如果是真的,它就会扩张到裹着do的身体。
对这个SO问题的公认答案:用于进度报告的Idiomatic Clojure ?在调试序列操作时非常有用。
Then there's something which is currently incompatible with swank-clojure's REPL, but is too good not to mention: debug-repl. You can use it in a standalone REPL, which is easy to get e.g. with Leiningen (lein repl); and if you're launching your programme from the command line, then it's going to bring its own REPL up right in your terminal. The idea is that you can drop the debug-repl macro in anywhere you like and have it bring up its own REPL when the programme's execution reaches that point, with all locals in scope etc. A couple of relevant links: The Clojure debug-repl, Clojure debug-repl tricks, how 'bout a debug-repl (on the Clojure Google group), debug-repl on Clojars.
swank-clojure在使SLIME的内置调试器在处理Clojure代码时发挥了足够的作用——注意stacktrace中不相关的部分是如何被灰色化的,因此很容易找到正在调试的代码中的实际问题。需要记住的一件事是,没有“名称标签”的匿名函数出现在堆栈跟踪中,基本上没有附加任何有用的信息;当添加“name标签”时,它确实会出现在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)))
我把这个插入我的用户。clj,所以在所有REPL会话中都可用。
我有一个调试宏,我觉得非常有用:
;;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)
下面是一个调试复杂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)))
...以及一篇解释其用途的文章。
推荐文章
- 如何为KnockoutJS调试模板绑定错误?
- 如何在构建目标之外生成gcc调试符号?
- 函数式编程有软件工程方法论吗?
- 调试器是如何工作的?
- 当有命令行参数时,如何使用GDB分析程序的核心转储文件?
- 如何在Visual Studio中找到堆栈跟踪?
- 有一个好的Valgrind Windows的替代品吗?
- Eclipse调试器总是阻塞在ThreadPoolExecutor上,没有任何明显的异常,为什么?
- Visual Studio:如何打破处理异常?
- 确保您的项目构建设置正在生成一个dSYM文件。对于所有配置,DEBUG_INFORMATION_FORMAT都应该设置为dwarf-with-dsym
- 如何获得GDB中所有线程的回溯?
- 如何检测IE11?
- 如何通过参数和重定向stdin从一个文件到程序运行在gdb?
- 如何在Clojure中创建web应用程序?
- 我如何调试git/git-shell相关的问题?