【问题标题】:Applying var or #' to a list of functions in Clojure将 var 或 #' 应用于 Clojure 中的函数列表
【发布时间】:2014-06-01 06:12:42
【问题描述】:

我正在尝试读取 Clojure 中函数集合的元数据,但 var 或 reader 特殊形式不起作用,除非它们直接处理符号。

; this works
(var my-fn)

; this doesn't
(defn val-it [x] (var x))
(val-it my-fn)

有没有办法让它在另一个函数的范围内工作?

【问题讨论】:

    标签: clojure functional-programming


    【解决方案1】:

    resolve 返回当前命名空间上下文中与给定符号对应的 Var 或类对象。 ns-resolve 允许您指定在哪个命名空间中解析符号。

    (resolve 'my-fn)
    ;= #'some.ns/my-fn
    

    如果符号无法解析为 Var,则返回 nil

    【讨论】:

    • 不适用于循环:(map resolve ['one 'two 'three])
    • 是的:(map resolve '[+ - /]) => (#'clojure.core/+ #'clojure.core/- #'clojure.core//)。您尝试过哪些没有按预期工作的方法?
    【解决方案2】:

    (var my-fn)确实直接处理符号,因为它是一种特殊形式(读者收到未评估的形式)。

    您要读取的元数据存储在var 对象中,而不是函数对象中。

    因此,您的目标,即从函数对象列表中读取元数据,只能通过遍历所有现有变量并通过相等性比较它们的值来实现。如果函数对象是唯一的开始方式,我只会推荐它。

    (defn meta-of
      "Returns a hashmap mapping the function objects in fn-objs to 
      a set of metadata of vars containing it."
      [fn-objs]
      (let [fn-objs (set fn-objs)]
        (reduce (fn [acc ns]
                  (reduce (fn [acc var]
                            (let [val (var-get var)]
                              (cond-> acc
                                      (contains? fn-objs val)
                                      (update-in [val] (fnil conj #{}) (meta var)))))
                          acc
                          (vals (ns-interns ns)))) {} (all-ns))))
    (def foo inc) ;; Now there are two vars that have the inc function as their value
    (meta-of [inc])
    {#<core$inc clojure.core$inc@66d7e31d>  ;; <- function object
     #{{:ns #<Namespace clojure.core>,  ;; <- metadata in clojure.core namespace
        :name inc,
        :file "clojure/core.clj",
        :column 1,
        :line 881,
        :arglists ([x]),
        :added "1.2",
        :inline #<core$inc__inliner clojure.core$inc__inliner@24f87069>,
        :doc
        "Returns a number one greater than num. Does not auto-promote\n  longs, will throw on overflow. See also: inc'"}
       {:ns #<Namespace user>,  ;; <- metadata in user namespace
        :name foo,
        :file "/tmp/form-init1078564431656334911.clj",
        :column 1,
        :line 1}}}
    

    【讨论】:

    • 我想可以使用clj-stacktrace 方法来猜测定义函数的名称,然后,如果它似乎是顶级的,则直接查找该 Var。不过,它确实感觉有点 hacky。
    • @MichałMarczyk 这也是个好主意。它肯定会在性能方面做得更好。
    猜你喜欢
    • 2018-12-19
    • 1970-01-01
    • 2020-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多