当使用Java等命令式编程语言进行编程时,可以方便地添加跟踪语句。例如:
for (int i=0; i<10; i++) {
// do something
// do something
System.out.println("Some trace statement");
// do something
}
如何用Clojure等LISP方言实现这一目标 - 比如说我想在复发之前添加一条痕迹:
(def fact
(fn [n]
(loop [cnt n acc 1]
(if (zero? cnt)
acc
;; say I want to add a trace here
(recur (dec cnt) (* acc cnt))))))
注意:
答案 0 :(得分:5)
Lisp环境通常提供交互式调试环境和跟踪机制。 例如,在SBCL中,您可以使用trace macro: 您甚至不需要修改代码,就像您在Java示例中所做的那样。
对于Clojure,请查看tools.trace library或以下答案:clojure: adding a debug trace to every function in a namespace?
另请参阅此问题的许多答案:Debugging in Clojure? 其中大部分都涉及将要调试/跟踪的表达式嵌套在另一个表达式中,如 Chiron 建议。
我不认为&#34; 我必须重新格式化并正确关闭括号&#34;是一个很好的论点;每次编辑程序时都必须处理语法,否则您将无法修改代码。
我个人不使用我现在是Paredit的快乐用户。您的编辑在编码时会跟踪parens和括号,这非常方便。
我真的不想将你的表达式嵌入另一个表达式中,我想你可以编写一个读取器宏,这样你就可以使用debug语句注释一个表达式,但这是overkill,imho(编辑:这显然是spyscope所做的;请参阅NielsK的回答)。
答案 1 :(得分:5)
Spyscope库提供了一个简单的选项,可以放入跟踪打印而无需更改原始语法,就像您(和许多其他人)喜欢的那样。
spyscope.repl=> (take 20 (repeat #spy/p (+ 1 2 3)))
6
(6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6)
还有包含跟踪消息的方法
spyscope.repl=> #spy/d ^{:marker "triple-add"} (+ 1 2 3)
spyscope.repl$eval3935.invoke(NO_SOURCE_FILE:1) triple-add (+ 1 2 3) => 6
6
甚至(部分)堆栈跟踪
spyscope.repl=> (take 20 (repeat #spy/d ^{:fs 3} (+ 1 2 3)))
----------------------------------------
clojure.lang.Compiler.eval(Compiler.java:6477)
clojure.lang.Compiler.eval(Compiler.java:6511)
spyscope.repl$eval675.invoke(REPL:13) (+ 1 2 3) => 6
(6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6)
答案 2 :(得分:2)
使用do
阻止:
(def fact
(fn [n]
(loop [cnt n acc 1]
(if (zero? cnt)
acc
(do
(println "**")
(recur (dec cnt) (* acc cnt)))))))
user=> (fact 4)
**
**
**
**
24
在你的REPL中:
(doc do)
DO (做exprs *)
特殊表格
按顺序计算表达式并返回值 最后。如果未提供表达式,则返回nil。 请参阅http://clojure.org/special_forms#do
答案 3 :(得分:1)
我有几个允许这样的东西的宏(因为它很方便,不必多次抛出预测):
(my-trace(“hi mom”1 2 3)recur(dec cnt)(* acc cnt))
my-trace只是扩展为:
(progn这个 (命令跟踪“嗨妈妈”1 2 3) (recur(dec cnt)(* acc cnt)))
更简单的变体是:
(echo hi-mom recur(dec cnt)(* acc cnt))
捕获包装表单的结果并打印出用hi-mom标记的内容。
我可以插入/删除nil作为未评估的第一个参数来关闭/打开跟踪。在一些调试输出中,我只是将跟踪保留在原位,但禁用,直到下次我需要它。
-hk