dan*_*lmo 3 symbols function clojure name-lookup
假设我将x定义为符号函数foo
(defn foo [x] x)
(def x foo)
Run Code Online (Sandbox Code Playgroud)
如果只给出x,可以发现名称"foo"吗?
在foo中有没有办法查找函数x的名称 - 在这种情况下是"foo"?
(foo x)
Run Code Online (Sandbox Code Playgroud)
是否有可能创建一个功能,如:
(get-fn-name x)
foo
Run Code Online (Sandbox Code Playgroud)
最近在这个网站上提出了类似的问题; 看到这里
当你这样做时(def x foo),你将x定义为"值为foo",而不是" foo自身".一旦foo解决了它的价值,那个价值就不再有任何关系了foo.
所以也许你现在可以看到一个可能的问题答案:foo当你去做定义时不要解决x.而不是......
(def x foo)
Run Code Online (Sandbox Code Playgroud)
...做...
(def x 'foo)
Run Code Online (Sandbox Code Playgroud)
现在,如果你试图得到它的值x,你将得到foo(字面上),而不是foo解析的值.
user> x
=> foo
Run Code Online (Sandbox Code Playgroud)
但是,这可能会有问题,因为您可能有时也希望能够获得foo解析为使用的值x.但是,您可以通过执行以下操作来执行此操作:
user> @(resolve x)
=> #<user$foo user$foo@157b46f>
Run Code Online (Sandbox Code Playgroud)
如果我要描述它的作用是什么:"将值x解析为,将其用作符号,然后将该符号解析为其var(而不是其值),并取消引用该var以获取值".
......现在让我们做一些hacky.我不确定我是否会建议做我要提出的这些事情中的任何一个,但你确实提出过Can the name "foo" be discovered if only given x?,我可以想到两种方法可以做到这一点.
方法1:正则表达式的FN变量名称
说明什么foo,并x都解决以下:
(defn foo [a] (println a))
(def x foo)
user> foo
=> #<user$foo user$foo@1e2afb2>
user> x
=> #<user$foo user$foo@1e2afb2>
Run Code Online (Sandbox Code Playgroud)
现在,看看这个:
user> (str foo)
=> "user$foo@1e2afb2"
user> (str x)
=> "user$foo@1e2afb2"
Run Code Online (Sandbox Code Playgroud)
凉.这只能起作用,因为它foo解析为一个函数,它恰好具有类似var的名称,这个名称将是相同的,x因为它引用了相同的函数.请注意,"foo"包含在由(str x)(以及也由(foo x))生成的字符串中.这是因为函数的var名称显然是通过对用于最初定义它的符号的一些向后引用而创建的.我们将使用这个事实从任何函数中找到这个符号.
所以,我写了一个正则表达式,在函数var name的字符串中找到"foo".它并不是它寻找"foo",而是它寻找任何子字符串 - 用正则表达式术语,".*"- 前面是一个\$字符 - 用正则表达式术语"(?<=\$)"- 然后是\@字符 -在正则表达式中"(?=@)"...
user> (re-find #"(?<=\$).*(?=@)"
(str x))
=> "foo"
Run Code Online (Sandbox Code Playgroud)
我们可以通过简单地环绕(symbol ...)它来进一步将其转换为符号:
user> (symbol (re-find #"(?<=\$).*(?=@)"
(str x)))
=> foo
Run Code Online (Sandbox Code Playgroud)
此外,整个过程可以推广到一个函数,该函数将接受一个函数并返回与该函数的var名称相关联的符号 - 这是在最初定义函数时给出的符号(此过程根本不适用于匿名函数).
(defn get-fn-init-sym [f]
(symbol (re-find #"(?<=\$).*(?=@)" (str f))))
Run Code Online (Sandbox Code Playgroud)
...或者我发现阅读更好......
(defn get-fn-init-sym [f]
(->> (str f)
(re-find #"(?<=\$).*(?=@)")
symbol))
Run Code Online (Sandbox Code Playgroud)
现在我们可以......
user> (get-fn-init-sym x)
=> foo
Run Code Online (Sandbox Code Playgroud)
方法#2:根据身份反向查找所有ns映射
这将很有趣.
因此,我们将采用所有命名空间映射,然后dissoc 'x从中获取,然后根据每个映射的val是否与解析的内容完全相同来过滤剩余的内容x.我们将在过滤后的序列中采取第一件事,然后我们将在第一件事情上取得钥匙以获得符号.
user> (->> (dissoc (ns-map *ns*) 'x)
(filter #(identical? (let [v (val %)]
(if (var? v) @v v))
x))
first
key)
=> foo
Run Code Online (Sandbox Code Playgroud)
请注意,如果您替换x为foo上述内容,则可以获得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))
Run Code Online (Sandbox Code Playgroud)
这里的主要区别是参数必须是带引号的符号.
user> (find-equiv-sym 'x)
=> foo
Run Code Online (Sandbox Code Playgroud)
这个find-equiv-sym功能真的不太好.当命名空间中的多个内容解析为相同的值时,会发生问题.您可以返回解析为相同内容的符号列表(而不是仅返回第一个符号),然后从那里进一步处理它.更改当前函数以使其工作很简单:删除最后两行(first和key),并替换它们(map key).
无论如何,我希望这对你来说和我一样有趣和有趣,但我怀疑这些黑客中的任何一个是不是一个很好的办法.我主张我的第一个解决方案.