如何从clojure中的符号获取函数的名称? [英] How can I get the name of a function from a symbol in clojure?

查看:127
本文介绍了如何从clojure中的符号获取函数的名称?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 (defn foo [x] x)
$ b如果只给出x,那么可以发现名字foo吗?

p>

在foo中有一种方法来查找函数x - foo的名称吗?

 (foo x)

创建一个函数,如:

 (get-fn-name x)
foo


解决方案

请参阅此处



当你执行(def x foo)时,你将x定义为 foo ,而不是 foo 本身。一旦 foo 已经解析为其值,该值就不再与 foo 有任何关系。



因此,也许你现在可以看到一个可能的答案:在你走时不要解决 foo 定义 x 。而不是...

 (def x foo)

... do ...

 (def x'foo )

现在,如果您尝试获取 x ,你会得到 foo (字面上),而不是 foo 解析为

  user> x 
=> foo

然而,这可能是有问题的,因为你可能有时想要 foo 的值解析为使用 x 。但是,您可以这样做:

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

如果我描述这是什么,它将是:获取值 x 解析为,使用它作为符号,然后将该符号解析为其var(不是其值),并取消引用该var以获取值 p>

...现在让我们做一些hacky。我不确定我会建议做这些事情我建议,但你曾问如果只给出x,可以发现名字foo?

方法#1:regex fn var name

>

注意 foo x 都会解析为以下内容:

 (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 解析为一个函数,恰好有一个类似var的名称,这将是相同的 x 因为它指的是相同的函数。注意foo包含在(str x)(以及(foo x) )。这是因为函数的var名称显然是通过对用于最初定义它的符号的一些向后引用来创建的。我们将使用这个事实从任何函数中找到非常符号。



因此,我写了一个正则表达式在函数var name的字符串中找到foo。这不是它寻找foo,而是它寻找任何子字符串 - 在正则表达式术语中,。* - 前面由 \ $ 字符 - 在正则表达式字(?< = \ $) ,然后是 \ @ 字符 - 正则表达式(?= @) ...

  user> (重新查找#(?<= \ $)。*(?= @)
(str x))
= foo



我们可以进一步将其转换为符号,只需将符号...)周围:

  (符号(重新查找#(?<= \ $)。*(?= @)
(str x)))
= foo

此外,这个整个过程可以推广到一个函数,与该函数的var name相关联 - 这是函数最初定义时给出的符号(该过程对于匿名函数不会很好地工作)。

 (defn get-fn-init-sym [f] 
(符号(重新查找#(?<= \ $)。* str f))))

...或者这个我觉得更好阅读...

 (defn get-fn-init-sym [f] 
( - >>(str f)
(重新查找#(?<= \ $)。*(?= @))
symbol))

现在我们可以...

  user> (get-fn-init-sym x)
=>方法#2:基于身份反向查找所有ns映射 br>
这将是有趣的。



所以,我们将采取所有的命名空间映射,然后 dissoc 'x ,然后根据每个映射的val是否指向完全相同的 x 解析为。我们将采用过滤后的顺序中的第一件事,然后我们将取得第一件事的关键字,以获得符号。

  user> ( - >>(dissoc(ns-map * ns *)'x)
(过滤器# vv))
x))
first
key)
=> foo

请注意,如果您替换 x foo 上面,你会得到 x 。真的所有这些都是返回的名字,它发现映射到与 x 完全相同的值。和以前一样,这可以推广到一个函数:

 (defn find-equiv-sym [sym] 
- >>(dissoc(ns-map * ns *)sym)
(filter(相同? )
@(resolve sym)))
first
key))

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

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

find-equiv-sym 函数真的不是很好。当命名空间中有多个东西解析为相同的值时,就会出现问题。您可以返回解析为相同内容的符号列表(而不是只返回第一个符号),然后从那里进一步处理。更改当前函数以使其工作很简单:删除最后两行(第一) ,并替换为(map key)



无论如何,我希望这是有趣和有趣的因为它是对我来说,但我怀疑这些黑客是否会是一种良好的事情的方式。我主张我的第一个解决方案。


Suppose I define x as symbol function foo

(defn foo [x] x)

(def x foo)

Can the name "foo" be discovered if only given x?

Is there a way within foo to look up the name of the function x - "foo" in this case?

(foo x)

Is there or is it possible to create a function such as:

(get-fn-name x)
foo

解决方案

A similar question was asked recently on this site; see here

When you do (def x foo), you are defining x to be "the value at foo", not "foo itself". Once foo has resolved to its value, that value no longer has any relationship whatsoever to foo.

So maybe you see one possible answer to your question now: don't resolve foo when you go to do define x. Instead of doing...

(def x foo)

...do...

(def x 'foo)

Now if you try to get the value of x, you will get foo (literally), not the value that foo resolves to.

user> x
=> foo

However, that is likely problematic, because you will probably also sometimes want to be able to get at the value that foo resolves to using x. However however, you would be able to do this by doing:

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

If I were to describe what this does it would be: "get the value x resolves to, use that as a symbol, then resolve that symbol to its var (not its value), and dereference that var to obtain a value".

...Now let's do something hacky. I'm not sure I would advise doing either of these things I'm about to suggest, but you did ask Can the name "foo" be discovered if only given x?, and I can think of two ways you could do that.

Method #1: regex the fn var name
Notice what foo and x both resolve to below:

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

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

Now, check this out:

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

Cool. This only works because foo resolves to a function, which happens to have a var-like name, a name which will be the same for x because it refers to the same function. Note that "foo" is contained within the string produced by (str x) (and also by (foo x)). This is because the function's var name is apparently created with some backwards reference to the symbol that was used to initially define it. We're going to use this fact to find that very symbol from any function.

So, I wrote a regular expression to find "foo" inside that string of the function var name. It isn't that it looks for "foo", but rather that it looks for any sub-string--in regex terms, ".*"--that is preceded by a \$ character--in regex terms "(?<=\$)"--and followed by the \@ character--in regex terms "(?=@)"...

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

We can further convert this to a symbol by simply wrapping (symbol ...) around it:

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

Furthermore, this whole process could be generalized to a function that will take a function and return the symbol associated with that function's var name--which is the symbol was given when the function was initially defined (this process will not at all work nicely for anonymous functions).

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

...or this which I find nicer to read...

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

Now we can do...

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

Method #2: reverse lookup all ns mappings based on identity
This is going to be fun.

So, we're going to take all the namespace mappings, then dissoc 'x from it, then filter what remains based on whether the val at each mapping refers to the exact same thing as what x resolves to. We'll take the first thing in that filtered sequence, and then we'll take the key at that first thing in order to get the symbol.

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

Notice that if you replaced x with foo above, you would get x. Really all this is doing is returning the first name it finds that maps to the exact same value as x. As before, this could be generalized to a function:

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

The main difference here is that the argument will have to be a quoted symbol.

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

This find-equiv-sym function is really not very good. Problems will happen when you have multiple things in the namespace resolving to identical values. You could return this list of symbols that resolve to identical things (instead of just returning the first one), and then process it further from there. It would be simple to change the current function to make this work: delete the last two lines (first and key), and replace them with (map key).

Anyways, I hope this was as fun and interesting for you as it was for me, but I doubt whether either of these hacks would be a good way of going about things. I advocate my first solution.

这篇关于如何从clojure中的符号获取函数的名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆