轻松地将跟踪语句添加到LISP方言(例如,不使用do)

时间:2014-04-07 08:05:03

标签: clojure lisp

当使用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))))))

注意:

  1. 该方法应该比添加一行简单
  2. 例如,如果我要使用do块 - 我必须重新格式化,确保我正确地关闭括号

4 个答案:

答案 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

我个人不使用我现在是Paredit的快乐用户。您的编辑在编码时会跟踪parens和括号,这非常方便。

Reader宏

我真的不想将你的表达式嵌入另一个表达式中,我想你可以编写一个读取器宏,这样你就可以使用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