【发布时间】:2021-05-06 18:37:15
【问题描述】:
我正在编写一个宏,它会查看给定符号的元数据并删除任何不是关键字的条目,即键名不以“:”开头,例如
(meta (var X)) ;; Here's the metadata for testing...
=>
{:line 1,
:column 1,
:file "C:\\Users\\Joe User\\AppData\\Local\\Temp\\form-init11598934441516564808.clj",
:name X,
:ns #object[clojure.lang.Namespace 0x12ed80f6 "thic.core"],
OneHundred 100,
NinetyNine 99}
我想删除条目“OneHundred”和“NinetyNine”,而保留其余元数据不变。
所以我有一些有效的代码:
(let [Hold# (meta (var X))] ;;Make a copy of the metadata to search.
(map (fn [[kee valu]] ;;Loop through each metadata key/value.
(if
(not= \: (first (str kee))) ;; If we find a non-keyword key,
(reset-meta! (var X) (dissoc (meta (var X)) kee)) ;; remove it from X's metadata.
)
)
Hold# ;;map through this copy of the metadata.
)
)
它有效。 “OneHundred”和“NinetyNine”的条目从 X 的元数据中消失了。
然后我把它编码成一个宏。上帝保佑 REPL。
(defmacro DelMeta! [S]
`(let [Hold# (meta (var ~S))] ;; Hold onto a copy of S's metadata.
(map ;; Scan through the copy looking for keys that DON'T start with ":"
(fn [[kee valu]]
(if ;; If we find metadata whose keyname does not start with a ":"
(not= \: (first (str kee)))
(reset-meta! (var ~S) (dissoc (meta (var ~S)) kee)) ;; remove it from S's metadata.
)
)
Hold# ;; Loop through the copy of S's metadata so as to not confuse things.
)
)
)
使用 defmacro 定义宏不会出错。
宏上的macroexpand-1,例如
(macroexpand-1 '(DelMeta! X))
扩展为正确的代码。这里:
(macroexpand-1 '(DelMeta! X))
=>
(clojure.core/let
[Hold__2135__auto__ (clojure.core/meta (var X))]
(clojure.core/map
(clojure.core/fn
[[thic.core/kee thic.core/valu]]
(if
(clojure.core/not= \: (clojure.core/first (clojure.core/str thic.core/kee)))
(clojure.core/reset-meta! (var X) (clojure.core/dissoc (clojure.core/meta (var X)) thic.core/kee))))
Hold__2135__auto__))
但是!!!
实际上在 REPL 中使用实参调用宏会发出最难以理解的错误消息:
(DelMeta! X) ;;Invoke DelMeta! macro with symbol X.
Syntax error macroexpanding clojure.core/fn at (C:\Users\Joe User\AppData\Local\Temp\form-init11598934441516564808.clj:1:1).
([thic.core/kee thic.core/valu]) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
(thic.core/kee thic.core/valu) - failed: Extra input at: [:fn-tail :arity-n :params] spec: :clojure.core.specs.alpha/param-list
哦,全能而睿智的Clojuregods,我恳求你怜悯。 我的罪在哪里?
【问题讨论】:
-
我开始写一个答案,但停止了,因为它真的不清楚你到底想达到什么目标。一些提示:我没有得到相同的异常,而是得到一个异常,告诉我“kee”不存在——你想使用
kee#(如果你想使用它,valu#)。阅读变量捕获,例如在Clojure for the Brave and True。其次,当您想要的只是副作用时,您调用map,使用doseq更清楚地表达了这一点。但主要问题是,为什么您首先会有非关键字元数据? -
map在这里是错误的,但doseq也是如此,因为两个更新会相互干扰。而是使用reduce或into/for在功能上构建新地图,然后设置一次。 -
我觉得结尾很有趣哈哈
标签: intellij-idea clojure metadata cursive