【问题标题】:How can I display the definition of a function in Clojure at the REPL?如何在 REPL 中显示 Clojure 中的函数定义?
【发布时间】:2010-09-23 22:29:37
【问题描述】:

我正在寻找让 REPL 打印函数的当前定义的能力。有没有办法做到这一点?

例如,给定:

(defn foo [] (if true "true"))

我想说几句

(print-definition foo)

并得到类似的东西

(foo [] (if true "true"))

打印出来的。

【问题讨论】:

    标签: clojure


    【解决方案1】:

    source 的替代方案(从1.2.0 开始,在启动 REPL 时应该可以通过clojure.repl/source 获得。如果您使用的是1.1.0 或更低版本,则source 位于clojure.contrib.repl-utils 中。 ),用于 REPL,而不是查看 .clj 文件中定义的函数:

    (defmacro defsource
      "Similar to clojure.core/defn, but saves the function's definition in the var's
       :source meta-data."
      {:arglists (:arglists (meta (var defn)))}
      [fn-name & defn-stuff]
      `(do (defn ~fn-name ~@defn-stuff)
           (alter-meta! (var ~fn-name) assoc :source (quote ~&form))
           (var ~fn-name)))
    
    (defsource foo [a b] (+ a b))
    
    (:source (meta #'foo))
    ;; => (defsource foo [a b] (+ a b))
    

    一个简单的print-definition

    (defn print-definition [v]
      (:source (meta v)))
    
    (print-definition #'foo)
    

    #' 只是一个reader macro,从#'foo 扩展到(var foo)

    (macroexpand '#'reduce)
    ;; => (var reduce)
    

    【讨论】:

    • 去掉defsource关键字最好是掉头。 (alter-meta! (var ~fn-name) assoc :source (drop 1 (quote ~&form))
    【解决方案2】:

    您需要导入 repl 命名空间,并从中使用 source 函数:

    (ns myns
        (:use [clojure.repl :only (source)]))
    (defn foo [] (if true "true"))
    (source foo)
    
    => (foo [] (if true "true"))
        nil
    

    虽然这在 REPL 中不起作用,但只有在类路径上的 .clj 文件中定义函数时才起作用。那么,这不能回答您的问题:您需要有一个defn,它在它定义的fn 的元数据中存储函数的来源。然后,您将编写一个函数来调用该元数据。这应该不是很困难。

    【讨论】:

      【解决方案3】:

      Clojure 没有反编译器,这意味着除非它是从磁盘加载的 defn,否则无法获取任意函数的源代码。但是,您可以使用一种称为 serializable-fn 的巧妙技巧来创建一个将其源形式存储在其元数据中的函数:http://github.com/Seajure/serializable-fn

      defsource 的答案与此非常相似,但此解决方案适用于任意 fns,而不仅仅是顶级 defns。它还使 fns 在 repl 上打印得很漂亮,而无需特殊的打印功能。

      【讨论】:

        【解决方案4】:

        在 clojure 1.2 的 REPL 中,source 函数立即可用。你可以这样使用它:

        $ java -cp clojure.jar clojure.main
        Clojure 1.2.0
        用户=> (源 slurp)
        (定义啜饮
          "使用编码enc将f命名的文件读入字符串
          并将其归还。”
          {:添加“1.0”}
          ([f & opts]
             (让 [opts (normalize-slurp-opts opts)
                   某人 (StringBuilder.)]
               (with-open [#^java.io.Reader r (应用 jio/reader f opts)]
                 (循环 [c (.read r)]
                   (如果(否定?c)
                     (str 某人)
                     (做
                       (.append sb (char c))
                       (recur (.read r)))))))))
        零
        用户=>

        其他一些函数也会从 clojure.repl 库自动导入到 REPL 的 user 命名空间中。请参阅 API 文档here

        但是,正如此处其他答案所指出的,您不能按原样使用 source 来打印您在 REPL 中定义的函数。

        【讨论】:

          【解决方案5】:

          我最近在 Clojure 邮件列表上准确地询问了这个问题,答案包括重写部分 REPL 以隐藏输入(和输出)以供将来参考,以及重写 defn 以将源存储在元数据中(其中然后您可以轻松地在 REPL 中检索)。

          Read the thread on the Clojure mailing list

          【讨论】:

          • -1,如果您在此处包含更详尽的解释,此答案可能会更好,请参阅meta。这个答案并没有真正提供具体的处方。
          猜你喜欢
          • 2011-08-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多