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

use*_*468 3 lisp clojure

当用Java等命令式编程语言编程时,可以方便地添加跟踪语句.例如:

for (int i=0; i<10; i++) {
  // do something
  // do something
  System.out.println("Some trace statement");
  // do something 
}
Run Code Online (Sandbox Code Playgroud)

如何通过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))))))
Run Code Online (Sandbox Code Playgroud)

笔记:

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

cor*_*ump 5

非侵入性追踪

Lisp环境通常提供交互式调试环境和跟踪机制.例如,在SBCL中,您可以使用跟踪宏: 您甚至不需要像在Java示例中那样修改代码.

对于Clojure,请查看tools.trace库或以下答案:clojure:向命名空间中的每个函数添加调试跟踪?

自定义函数和宏

另请参阅此问题的许多答案:在Clojure中进行调试? 其中大多数都涉及将要调试/跟踪的表达式嵌套在另一个表达式中,如Chiron建议的那样.

我不认为" 我必须重新格式化并适当地关闭括号 "是一个很好的论点; 每次编辑程序时都必须处理语法,否则您将不会修改代码.

Paredit

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

读者宏

我真的不想把你的表达式嵌入到另一个表达式中,我想你可以编写一个读取器宏,这样你就可以用一个调试语句来注释一个表达式,但这有点矫枉过正,imho(编辑:这是spyscope所做的,显然;参见NielsK的答案).

  • TRACE是一个宏,而不是一个函数. (3认同)

Nie*_*lsK 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) 
Run Code Online (Sandbox Code Playgroud)

还有包含跟踪消息的方法

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
Run Code Online (Sandbox Code Playgroud)

甚至(部分)堆栈跟踪

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)
Run Code Online (Sandbox Code Playgroud)