首页 文章

在Clojure中调试? [关闭]

提问于
浏览
212

使用repl时,调试Clojure代码的最佳方法是什么?

14 回答

  • 44

    还有dotrace,它允许您查看所选功能的输入和输出 .

    (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
    
  • -4

    我有一个小调试宏,我觉得非常有用:

    ;;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)
    
  • 2

    Emacs的CIDER有一个源调试器,您可以通过Emacs缓冲区内的表达式逐步表达,甚至注入新值 . 你可以阅读所有相关信息here . 演示截图:

    CIDER debug

  • 143

    Here's用于调试复杂的 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)))
    

    ......和an essay explaining its use .

  • 8

    来自Java并熟悉Eclipse,我喜欢Counterclockwise(Clojure开发的Eclipse插件)提供的功能:http://doc.ccw-ide.org/documentation.html#_debug_clojure_code

  • 5

    我最喜欢的方法是在代码中自由地填充 println ... Turning them on and off is easy 感谢 #_ 阅读器宏(这使得读者以下面的形式阅读,然后假装它从未见过它) . 或者您可以使用宏扩展到传入的主体或 nil ,具体取决于某些特殊变量的值,例如 *debug*

    (defmacro debug-do [& body]
      (when *debug*
        `(do ~@body)))
    

    在那里有 (def *debug* false) ,这将扩展到 nil . 使用 true ,它将扩展为包含在 do 中的 body .


    这个SO问题的接受答案: Idiomatic Clojure for progress reporting? 在调试序列操作时非常有用 .


    然后有一些东西是 currently incompatible with swank-clojure 的REPL,但是太好了,更不用说了: debug-repl . 您可以在独立的REPL中使用它,这很容易获得,例如与莱宁根( lein repl );如果你要在自己的终端上自行安装REPL . 我们的想法是你可以将 debug-repl 宏放在你喜欢的任何地方,并在程序执行到达那一点时启动它自己的REPL,所有本地人都在范围内等 . 几个相关的链接:The Clojure debug-replClojure debug-repl trickshow 'bout a debug-repl(on Clojure Google小组),debug-repl on Clojars .


    使用Clojure代码时,swank-clojure能够使SLIME的内置调试器变得非常有用 - 请注意堆栈跟踪的不相关位是如何变灰的,因此很容易在被调试的代码中找到实际问题 . 要记住的一件事是,没有“名称标签”的匿名函数出现在堆栈跟踪中,基本上没有附加有用的信息;当添加“名称标签”时,它会出现在堆栈跟踪中,并且一切都很好:

    (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)
                       ^^^
    
  • 99

    您还可以使用Alex Osborne's 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)))
    

    我把它粘在我的user.clj中,所以它在所有REPL会话中都可用 .

  • 9

    “使用repl调试Clojure代码的最佳方法”

    稍微左侧的字段,但“使用REPL iteself” .

    我已经写了一年多的业余爱好者Clojure,并且对任何调试工具都没有太大的需求 . 如果你保持函数较小,并在REPL中使用预期输入运行每个函数并观察结果,那么应该可以清楚地了解代码的行为方式 .

    我发现调试器对于在正在运行的应用程序中观察STATE最有用 . Clojure使用不可变数据结构(没有改变状态)的函数式编写变得简单(而且有趣!) . 这大大减少了对调试器的需求 . 一旦我知道所有组件的行为与我期望的一样(特别注意事物的类型),那么大规模行为很少成为问题 .

  • 1

    如果您使用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

    正如上面的评论中提到的那样 .

  • 6

    对于IntelliJ,有一个很棒的Clojure插件叫做Cursive . 除此之外,它还提供了一个REPL,您可以在调试模式下运行并逐步执行您的Clojure代码,就像您所做的那样 . Java的 .

    我会说第二个Peter Westmacott的答案,因为根据我的经验,在REPL中运行我的代码片段大部分时间都是一种充分的调试形式 .

  • 55

    从2016年开始,您可以使用Debux,一个用于Clojure / Script的简单调试库,它与您的repl以及浏览器控制台一起使用 . 您可以在代码中添加 dbg (调试)或 clog (console.log)宏,并轻松观察打印到REPL和/或控制台的各个函数的结果等 .

    从项目的Readme

    基本用法这是一个简单的例子 . 宏dbg打印一个原始表单,并在REPL窗口上漂亮地打印评估值 . 然后它返回值而不会干扰代码执行 . 如果用这样的dbg包装代码,(* 2(dbg(10 20))); => 60将在REPL窗口中打印以下内容 . REPL输出:dbg:(10 20)=> 30嵌套dbg dbg宏可以嵌套 . (dbg(* 2(dbg(10 20)))); => 60 REPL输出:dbg:(10 20)=> 30
    dbg:(* 2(dbg(10 20)))=> 60

  • 35

    Hugo Duncan和合作者继续为ritz项目做出惊人的工作 . Ritz-nrepl是具有调试功能的nREPL服务器 . 在Clojure / Conj 2012上观看Hugo的Debuggers in Clojure演讲,看看它在行动中,在视频中一些幻灯片是不可读的,所以你可能想要查看here的幻灯片 .

  • 2

    使用spyscope实现自定义读取器宏,以便您的调试代码也是 生产环境 代码https://github.com/dgrnbrg/spyscope

  • 13

    def-let的函数版本,它将let转换为一系列defs . 一些功劳归于here

    (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)])
    

相关问题