如何获得函数';Clojure中的字符串是什么?
如何在Clojure中以字符串形式获取函数名 到目前为止,我所讲的内容看起来都不地道:如何获得函数';Clojure中的字符串是什么?,clojure,Clojure,如何在Clojure中以字符串形式获取函数名 到目前为止,我所讲的内容看起来都不地道: (defn fn-name [f] (first (re-find #"(?<=\$)([^@]+)(?=@)" (str f)))) (defn foo []) (fn-name foo) ;; returns "foo" 对于使用defn表单定义的函数: user> (-> #'map meta :name) map user> (defn nothing [&
(defn fn-name
[f]
(first (re-find #"(?<=\$)([^@]+)(?=@)" (str f))))
(defn foo [])
(fn-name foo) ;; returns "foo"
对于使用
defn
表单定义的函数:
user> (-> #'map meta :name)
map
user> (defn nothing [& _])
#'user/nothing
user> (-> #'nothing meta :name)
nothing
这需要访问var,而不仅仅是var持有的函数值。编辑:我找到了一个更好的方法
Clojure包含一个名为demunge的函数。因此,与其重新发明轮子,不如使用它;)
或者如果你想要一个漂亮的版本
(defn- pretty-demunge
[fn-object]
(let [dem-fn (demunge (str fn-object))
pretty (second (re-find #"(.*?\/.*?)[\-\-|@].*" dem-fn))]
(if pretty pretty dem-fn)))
(pretty-demunge map?);; "clojure.core/map?"
我认为有一种更干净的方法可以做到这一点。我遇到了同样的问题,想知道作为函数中的参数得到的函数的名称。我无法使用宏,因为我需要映射它,所以这里是我得到的:(假设字符串?函数是否作为参数传递) 当然,这不是一个完整的解决方案,但我相信你可以从这里解决问题。基本上Clojure会将函数名转换为它可以使用的名称。因此,基本思路显然是:你需要解开它! 这很容易,因为str可以处理任何函数:D,返回函数的损坏名称 顺便说一下,这也行
(def foo string?)
(clojure.string/replace (second (re-find #"^.+\$(.+)\@.+$" (str foo)))
#"\_QMARK\_" "?")
; string?
玩得开心我在评论中建议改进@carocad的答案。因为我也需要这样做,而且我需要在
clj
和cljs
中这样做,所以我想到了以下几点:
(ns my-ns.core
(:require [clojure.string :as s]
#?(:clj [clojure.main :refer [demunge]])))
(defn fn-name
[f]
#?(:clj
(as-> (str f) $
(demunge $)
(or (re-find #"(.+)--\d+@" $)
(re-find #"(.+)@" $))
(last $))
:cljs
(as-> (.-name f) $
(demunge $)
(s/split $ #"/")
((juxt butlast last) $)
(update $ 0 #(s/join "." %))
(s/join "/" $))))
请注意,cljs.core
有自己的demange
内置,无法访问clojure.main
编辑 请注意,如果执行
(使用meta a-fn{…})
,它将在clojure中返回clojure.lang.AFunction
,这将隐藏底层名称和命名空间信息。可能还有其他类似的情况,我不确定。使用fn
literal表单,您可以执行^{…}(fn…
而不是(使用meta(fn…{…})
,并且它不会返回clojure.lang.AFunction
,但该解决方法无法使用预定义函数。我还没有在clojurescript中测试过这一切,看看它是否以同样的方式工作
还请注意,clojure中的匿名函数总是以fn
结尾,就像中的“my ns.core/fn”
一样。通常,这些函数的末尾会有一个“”--[0-9]+”
,但上面的正则表达式删除了这个选项。您可以修改上面的函数,并对匿名函数进行例外。只要lambda具有内部名称,就会使用该名称,例如:
(fn-name (fn abc [x] x)) ;;=> "my-ns.core/abc"
同样,我还没有在clojurescript中测试过这些注释。在这种情况下,对var的访问是一个限制,因为这意味着我无法将这些代码封装到函数中。但是,根据您的提示(以及其他地方的提示),我创建了一个宏来完成这项工作。见我编辑的问题。您对这种解决方案有何看法?它看起来很合理,只要您没有像
(fn[f](fn name f))
这样的场景,因为var将在参数求值时解析。确切地说,我知道这一点,我想知道是否可以通过使用“完全限定”函数名传递宏来解决这个问题。什么是#map?我知道#{}for set和'is quote.#'foo
是一个读卡器宏,它扩展到(var foo)
-它访问命名空间拥有的可变变量,而不是存储在其中的值。一般来说,#
为函数引入读取器宏#()
,#{}
为集合引入读取器宏,#inst
为日期引入读取器宏,等等。这个宏有什么意义?它只能处理文本,如(fn name inc)
,而(let[f inc](fn name f))
将失败。如果您按字面意思键入inc
,只需键入“inc”
,然后保存一些字符!这就是它的局限性。正如@noisesmith提到的,它适用于同一名称空间中的defn
s。我不选择同时以字符串形式键入函数名。我正在寻找如何获取作为参数传递的任何函数的名称。这样的函数有名称吗?没有。正如Sean Devlin()所写,函数对象没有名称。只有函数绑定到的符号才有一个。我本来可以把我的问题说得更清楚的。哦,我刚刚意识到这几乎和你一开始的情况一样。我不明白它有什么问题。对我来说,它看起来非常好。正则表达式(.*?\/.[\-\-\-\-\@].*.在第一个-
处停止。例如,(pretty demange assoc in)
返回clojure.core/assoc
。如果您只得到@
之前的所有内容,我认为主要的边缘情况是字符串以#“-[0-9]+”
结尾(这是由lambdas引起的,例如当函数由定义时(def my fn(fn…)
)。因此,您需要检查两种可能性:(last(或(re-find#“(.+)--\d++”dem-fn)(re-find#“(.+)@”dem-fn))
。
(def foo string?)
(clojure.string/replace (second (re-find #"^.+\$(.+)\@.+$" (str foo)))
#"\_QMARK\_" "?")
; string?
(ns my-ns.core
(:require [clojure.string :as s]
#?(:clj [clojure.main :refer [demunge]])))
(defn fn-name
[f]
#?(:clj
(as-> (str f) $
(demunge $)
(or (re-find #"(.+)--\d+@" $)
(re-find #"(.+)@" $))
(last $))
:cljs
(as-> (.-name f) $
(demunge $)
(s/split $ #"/")
((juxt butlast last) $)
(update $ 0 #(s/join "." %))
(s/join "/" $))))
(fn-name (fn abc [x] x)) ;;=> "my-ns.core/abc"