【问题标题】:How do I dynamically find metadata for a Clojure function?如何动态查找 Clojure 函数的元数据?
【发布时间】:2020-01-23 17:43:26
【问题描述】:

假设我有以下代码:

(defn ^{:graph-title "函数 1"} func-1 [X] (用 x 做某事)) (定义获取图形标题 [功能] (字符串 ((元函数) :graph-title)))

我希望它返回“函数 1”,但它返回 nil。我认为这源于以下差异,我并不完全理解:

(元 func-1) => {:ns some-ns-info, :name func-1} (元#'func-1) => {:ns some-ns-info, :name func-1, :graph-title "功能 1"}

谁能给我解释一下?

【问题讨论】:

    标签: function clojure metadata clojure-contrib


    【解决方案1】:

    函数 func-1 上有元数据,Var #'func-1 上有元数据,符号 'func-1 上有元数据。 Clojure 阅读器宏^ 在读取时将元数据添加到符号defn 宏在编译时将元数据从 symbol 复制到 Var

    在 Clojure 1.2 之前,函数不支持元数据。在 Clojure 1.2 中,它们会这样做,并且defn 还将一些标准 Var 元数据复制到 函数

    Clojure 1.2.0
    user=> (defn ^{:foo :bar} func-1 [] nil) 
    #'user/func-1
    user=> (meta func-1)
    {:ns #<Namespace user>, :name func-1}
    user=> (meta #'func-1)
    {:foo :bar, :ns #<Namespace user>, :name func-1, ...
    

    但是,在当前的 Clojure 1.3 快照中,defn 不会将任何元数据复制到函数中:

    Clojure 1.3.0-master-SNAPSHOT
    user=> (defn ^{:foo :bar} func-1 [] nil) 
    #'user/func-1
    user=> (meta func-1)
    nil
    user=> (meta #'func-1)
    {:foo :bar, :ns #<Namespace user>, :name func-1, ...
    

    一般来说,如果您想获取定义的元数据,您需要 Var 上的元数据。

    【讨论】:

    • Leonel 的回答更接近我的要求,但感谢您的解释 - 解决了很多问题。
    • meta 不再复制到 fn 的任何已知原因?
    【解决方案2】:

    元数据附加到 var,而不是函数。

    因此,要获取图表标题,您必须从 var 的元数据中获取条目 :graph-title。你觉得你的宏怎么样?

    (defmacro get-graph-title
      [func]
      `(:graph-title (meta (var ~func))))
    
    (get-graph-title func-1)
    => "Function 1"
    

    【讨论】:

    • 或者只使用当前定义并执行 (get-graph-title #'func-1)
    【解决方案3】:

    虽然我不会说这是防弹的,但我会把它放在这里是为了繁荣,以防出于某种原因,你只有 Fn 对象,而不是它的符号或 Var。

    据我所知,当defn 求值时,它会生成一个名称与(munge fn-symbol) 相同的类。有一个名为demunge 的函数会为我们扭转这种情况。因此,从一个Fn对象的类名中,我们可以得到符号,从符号中,我们可以找到var。

    (-> (class some-fn)
        (print-str)
        (demunge)
        (symbol)
        (find-var)
        (meta))
    

    有趣的是,demunge 可以在 clojure.mainclojure.repl 中找到,而且它们的作用完全相同,因此请随意要求其中任何一个引入 demunge:

    (require '[clojure.main :refer (demunge)])
    ;; or
    (require '[clojure.repl :refer (demunge)])
    

    警告:

    正如我之前所说,首先尝试做:

    (meta #'some-fn)
    

    而不是我在此答案中显示的内容,因为它是正确的惯用机制。

    【讨论】:

      【解决方案4】:

      您在源代码中symbol func-1 上指定的元数据以def 特殊形式复制到名为func-1 的var。请参阅http://clojure.org/special_forms 中的 def 文档

      当您评估 func-1 时,这是一个绑定到 var 的符号,您会得到 var 的 (在这种情况下是函数对象)。见http://clojure.org/vars

      函数对象本身不会自动接收在符号/var上手动指定的元数据。

      所以,你想要的信息根本不在函数中。它在 var 中,你必须指定你真的想要 var func-1 本身而不是它的值。这就是 (var func-1) 和等效的快捷方式 #'func-1 所做的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多