如何在Clojure中将函数的名称作为字符串?
到目前为止我所看到的并不像惯用语那样:
(defn fn-name
[f]
(first (re-find #"(?<=\$)([^@]+)(?=@)" (str f))))
(defn foo [])
(fn-name foo) ;; returns "foo"
编辑:通过提供的提示,我将基本宏放在一起,完成了我想要的工作。它看起来更好吗?
(defmacro fn-name
[f]
`(-> ~f var meta :name str))
答案 0 :(得分:8)
对于使用defn
形式定义的函数:
user> (-> #'map meta :name)
map
user> (defn nothing [& _])
#'user/nothing
user> (-> #'nothing meta :name)
nothing
这需要访问var,而不仅仅是var保存的函数值。
答案 1 :(得分:4)
(clojure.repl/demunge (str map?));; "clojure.core/map?@2b68895c"
或者如果你想要一个美化版本
(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.string/replace (second (re-find #"^.+\$(.+)\@.+$" (str string?)))
#"\_QMARK\_" "?")
; string?
当然,这不是一个完整的解决方案,但我相信你可以从这里开始。基本上Clojure将函数名称变成了可以使用的东西。所以基本虽然显然是:你需要解决这个问题! 这很简单,因为str适用于任何函数:D,返回函数的错位名称。
顺便说一句,这也有效。
(def foo string?)
(clojure.string/replace (second (re-find #"^.+\$(.+)\@.+$" (str foo)))
#"\_QMARK\_" "?")
; string?
玩得开心
答案 2 :(得分:1)
我建议在评论中改进@ 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
内置了demunge
,无法访问clojure.main
。
修改强>
请注意,执行(with-meta a-fn {...})
时,它会在clojure中返回clojure.lang.AFunction
,它会隐藏基础名称和命名空间信息。可能还有其他情况,我不确定。使用fn
文字表单,您可以^{...} (fn ...)
代替(with-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中测试过这些笔记。
答案 3 :(得分:0)
我目前正在做的从回溯中提取函数名称如下:
[ERROR] Failed to execute goal on project get-content: Could not resolve dependencies for project project:cf:jar:1.0: Failed to collect dependencies at project:pipeline:jar:0.0.1: Failed to read artifact descriptor for project:pipeline:jar:0.0.1: Could not transfer artifact project:pipeline:pom:0.0.1 from/to project-nexus (XXX): Cannot access XXX with type default using the available connector factories: BasicRepositoryConnectorFactory: Cannot access XXX using the registered transporter factories: WagonTransporterFactory: Unsupported transport protocol -> [Help 1]
这似乎有效。我很感激这不是您所要求的,但可能有用。
已编辑
但这更干净,效果同样好:
(defn remove-anon
"Remove anonomous function elements from a `munged` name."
[^String munged]
(remove #(starts-with? % "fn__")
(split munged #"[\.\$]")))
(defn fn-name
"De-mung a `munged` function name"
[^String munged]
(replace (last (remove-anon munged)) "_" "-"))
(defn recover-function-name
[^StackTraceElement frame]
(when (ends-with? (.getFileName frame) ".clj")
(fn-name (.getClassName frame))))