【问题标题】:Why does dotrace throw a StackOverflowError here?为什么 dotrace 会在这里抛出 StackOverflowError?
【发布时间】:2010-06-05 06:06:12
【问题描述】:
(use '[clojure.contrib.trace])
(dotrace [str] (reduce str [\a \b]))

【问题讨论】:

    标签: clojure


    【解决方案1】:

    简而言之:

    那是因为trace-fn-calldotrace 用来包装要跟踪的函数)使用str 产生漂亮的TRACE foo => val 输出。

    扩展说明:

    dotrace 宏通过为每个包含要跟踪的函数的 Var 安装线程绑定来发挥其魔力;在这种情况下,有一个这样的 Var,clojure.core/str。替换看起来大致是这样的:

    (let [f @#'str]
      (fn [& args]
        (trace-fn-call 'str f args)))
    

    trace-fn-call,引用其文档字符串,“使用 args 跟踪对函数 f 的单个调用。”。在此过程中,它调用跟踪函数,记录返回值,打印出TRACE foo => val 形式的漂亮信息消息,并返回从跟踪函数获得的值,以便可以继续正常执行。

    如上所述,这个TRACE foo => val消息是使用str产生的;然而,在手头的情况下,这实际上是被跟踪的函数,所以对它的调用会导致对trace-fn-call 的另一个调用,它自己尝试使用str 生成跟踪输出字符串,这会导致另一个调用trace-fn-call... 最终导致堆栈爆炸。

    解决方法:

    dotracetrace-fn-call 的以下修改版本即使在核心 Vars 存在奇怪绑定的情况下也应该可以正常工作(请注意,期货可能不会及时安排;如果有问题,请参见下文):

    (defn my-trace-fn-call
      "Traces a single call to a function f with args.  'name' is the
      symbol name of the function."
      [name f args]
      (let [id (gensym "t")]
        @(future (tracer id (str (trace-indent) (pr-str (cons name args)))))
        (let [value (binding [*trace-depth* (inc *trace-depth*)]
                      (apply f args))]
          @(future (tracer id (str (trace-indent) "=> " (pr-str value))))
          value)))
    
    (defmacro my-dotrace
      "Given a sequence of function identifiers, evaluate the body
       expressions in an environment in which the identifiers are bound to
       the traced functions.  Does not work on inlined functions,
       such as clojure.core/+"
      [fnames & exprs]
      `(binding [~@(interleave fnames
                               (for [fname fnames]
                                 `(let [f# @(var ~fname)]
                                    (fn [& args#]
                                      (my-trace-fn-call '~fname f# args#)))))]
         ~@exprs))
    

    (在常规 dotrace 周围重新绑定 trace-fn-call 显然不起作用;我的猜测是这是因为 clojure.* Var 调用仍然被编译器硬连线,但这是另一回事。上面将工作,无论如何。)

    另一种方法是将上述my-dotrace 宏与my-trace-fn-call 函数一起使用,不使用期货,但修改为调用clojure.contrib.trace 函数的自定义替换,使用以下代码代替str

    (defn my-str [& args] (apply (.getRoot #'clojure.core/str) args))
    

    替换简单而乏味,我从答案中省略了它们。

    【讨论】:

    • 这应该被认为是一个错误,应该在哪里报告?或者我应该将 str 重新绑定到 smth 以使其工作?
    • 这似乎至少是一个文档错误,尽管我有一些想法可以对c.c.trace 进行非惊天动地的修改,这将使其在面对核心 Vars 反弹时更加健壮。 ..我可能会考虑测试一些设计。目前,我已经编辑了一个解决方法——我希望你可以接受。恐怕直接重新绑定任何东西都行不通。
    猜你喜欢
    • 2020-02-24
    • 1970-01-01
    • 2021-06-18
    • 2015-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-08
    相关资源
    最近更新 更多