我怎样才能从Clojure的一个符号的功能的名称?(How can I get the name o

2019-09-21 03:18发布

假设我定义x作为符号函数foo

(defn foo [x] x)

(def x foo)

可如果只给定x的名称为“foo”被发现的?

是否有foo中的方式来查找函数x的名字 - 在这种情况下,“富”?

(foo x)

是否有或是否有可能创建一个函数,例如:

(get-fn-name x)
foo

Answer 1:

类似的问题最近被问在这个网站; 看这里

当你这样做(def x foo) ,你x限定为“该值foo ”,而不是“ foo本身”。 一旦foo已决定它的值,这个值不再有任何任何关系foo

因此,也许你看到一个可能的回答你的问题现在:不解决foo当你去不定义x 。 而不是做...

(def x foo)

...做...

(def x 'foo)

现在,如果你尝试获得的值x ,你会得到foo (直译),而不是值foo解析。

user> x
=> foo

然而,这可能是有问题的,因为你可能有时也希望能够获得在该值foo解析为使用x 。 但是但是,您将能够做这样做:

user> @(resolve x)
=> #<user$foo user$foo@157b46f>

如果我来形容这也将是:“得值x解析,使用它作为一个符号,则解决符号及其变种( 不是它的价值),并取消引用该变种获得的价值。”

...现在让我们做一些哈克。 我不知道我会建议做任何的这些东西,我要建议,但是你没有问Can the name "foo" be discovered if only given x? ,我能想到的两种方法,你可以做到这一点。

方法1:正则表达式的FN变数名称
公告是什么foox都决心如下:

(defn foo [a] (println a))
(def x foo)

user> foo
=> #<user$foo user$foo@1e2afb2>
user> x
=> #<user$foo user$foo@1e2afb2>

现在,检查了这一点:

user> (str foo)
=> "user$foo@1e2afb2"
user> (str x)
=> "user$foo@1e2afb2"

凉。 这只能是因为foo解析为一个功能,这恰好有一个变种般的名字,这将是相同的名称x ,因为它指的是相同的功能。 需要注意的是“foo”被包含在由所生成的字符串中(str x)并且还通过(foo x) 这是因为该函数的变量名称显然与一些向后引用这是用于最初定义它的符号创建。 我们将利用这一点来找到任何功能非常的象征。

所以,我写了一个正则表达式查找功能变数名称的该字符串里面的“富”。 这并不是说,它看起来对“富”,而是它会查找任何串子-在正则表达式而言, ".*"凹口-前面有一个\$字符-在正则表达式的术语"(?<=\$)" -并且随后\@字符-在正则表达式条款"(?=@)" ...

user> (re-find #"(?<=\$).*(?=@)"
               (str x))
=> "foo"

我们可以通过简单的包装进一步将它转换为一个符号(symbol ...)它周围的:

user> (symbol (re-find #"(?<=\$).*(?=@)"
                       (str x)))
=> foo

此外,这整个过程可以概括为,将采取一个函数,返回与该函数的变量名称相关联的符号的功能 - 这是当最初定义的功能被赋予了符号(这个过程不会在所有的工作很好的匿名函数)。

(defn get-fn-init-sym [f]
  (symbol (re-find #"(?<=\$).*(?=@)" (str f))))

......这还是我找到更好的阅读...

(defn get-fn-init-sym [f]
  (->> (str f)
       (re-find #"(?<=\$).*(?=@)")
       symbol))

现在,我们可以做...

user> (get-fn-init-sym x)
=> foo

方法2:反向查询的NS映射基于身份
这将会非常好玩。

所以,我们要采取一切的命名空间的映射,然后dissoc 'x来自它,然后过滤剩下基于在每个映射瓦尔是否指的是完全一样的东西是什么x解析。 我们将采取的第一件事就是在过滤序列,然后我们将在第一次事情的关键,以获得该符号。

user> (->> (dissoc (ns-map *ns*) 'x)
           (filter #(identical? (let [v (val %)]
                                  (if (var? v) @v v))
                                x))
           first
           key)
=> foo

请注意,如果您更换xfoo上面,你会得到x 。 实际上这一切正在做的是返回它发现的第一个名称映射到的值完全相同的x 。 和以前一样,这可以概括为一个函数:

(defn find-equiv-sym [sym]
  (->> (dissoc (ns-map *ns*) sym)
       (filter #(identical? (let [v (val %)]
                              (if (var? v) @v v))
                            @(resolve sym)))
       first
       key))

这里的主要区别是,参数必须是引用符号。

user> (find-equiv-sym 'x)
=> foo

这个find-equiv-sym功能真的不是很好。 当你在命名空间分辨率为相同的值有多个问题的事情会发生。 你可以返回符号这个名单解析为相同的事情(而不是只返回第一个),然后从那里再处理它。 这将是简单的改变当前功能,使这项工作:删除最后两行( firstkey ),并与替换它们(map key)

不管怎么说,我希望这是有趣的和有趣的你,因为它是我的,但我怀疑是否这两种黑客将是处理事情的办法。 我主张我的第一个解决方案。



Answer 2:

目前尚不清楚你为什么会想这样做-当你做(def x foo)你实际上给名称x在您的命名空间的新变种。 它发生在具有相同的值foo (即它包含相同的功能),但以其他方式从富完全独立的。 这就像使用Java类比两个引用同一个对象。

为什么要继续想获得名foo

如果你真的想这样做类似这样的事情,这可能是在那里你可以使用一些自定义的元数据在其中包含原始符号功能的情况下:

(def foo 
  (with-meta
    (fn [x] x)
    {:original-function `foo}))

(def bar foo)

(defn original-function [v]
  "Returns the :original-function symbol from the metadata map"
  (:original-function (meta v)))

(original-function bar)
=> user/foo


文章来源: How can I get the name of a function from a symbol in clojure?