我定义了一个宏来将从字符串派生的符号绑定到字符串,如下所示:
lein repl
... Clojure 1.8.0 ...
user=> (defmacro foo [s] `(def ~(symbol s) ~s))
#'user/foo
在顶级调用时,它按预期工作:
user=> (foo "asdf")
#'user/asdf
user=> asdf
"asdf"
但是当我尝试映射一个在序列上调用宏的函数时,宏会绑定函数参数符号而不是我想要的函数:
user=> (map (fn [x] (foo x)) ["qwer"])
(#'user/x)
user=> x
"qwer"
user=> qwer
CompilerException ... Unable to resolve symbol: qwer ...
以下替代方法绑定Clojure创建的临时符号:
user=> (map #(foo %) ["qwer"])
(#'user/p1__1253#)
根据我在StackOverflow上研究的一些现有答案的建议,在doall
中包含它时也不起作用。
如何定义一个符号绑定宏,我可以在一个字符串集合上映射(在函数中或其他方面)?
答案 0 :(得分:3)
map
是一个函数,foo
是一个宏。由于宏扩展在编译时发生并且函数在运行时执行,因此定义一个可以映射(因此在运行时扩展)的符号绑定宏是不可能的。
你可以做的是这样的事情:
(defn foo2 [s]
`(def ~(symbol s) ~s))
(defmacro foos [ss]
`(do ~@(map foo2 ss)))
(foos ["asdf" "qwer"])
asdf ;; => "asdf"
qwer ;; => "qwer"
现在反过来了:使用函数map
和foo
扩展宏。
答案 1 :(得分:2)
这是一种做法。解决方案首先显示 宏 foo
如何工作,然后使用带有 功能的中间解决方案 map-foo-fn
然后eval
。
最终解决方案使用第二个 宏 map-foo-mcr
。这似乎是必要的,因为(def ...)
是一种特殊形式。这与“乌龟一直向下”的问题类似(但不完全相同),在一个地方使用宏需要所有呼叫者也是宏,而不是函数。
(ns clj.core
(:require
[tupelo.core :as t] ))
(t/refer-tupelo)
(defmacro foo
[arg]
`(def ~(symbol arg) ~arg))
(foo "aa")
(spyx aa)
(defn map-foo-fn
[coll]
(cons 'do
(forv [elem coll]
(list 'foo elem))))
(newline)
(prn (map-foo-fn ["bb"] ))
(eval (map-foo-fn ["bb"] ))
(spyx bb)
(defmacro map-foo-mcr
[coll]
`(do
~@(forv [elem coll]
(list 'foo elem))))
(newline)
(println (macroexpand-1 '(map-foo-mcr ["cc" "dd"] )))
(map-foo-mcr ["cc" "dd"] )
(spyx cc)
(spyx dd)
结果:
aa => "aa"
(do (foo "bb"))
bb => "bb"
(do (foo cc) (foo dd))
cc => "cc"
dd => "dd"
请记住,虽然宏 可以 做一件功能无法做到的事情(避免arg评估),但宏 不能 < / strong>做其他功能可以做的事情。特别是,宏不能传递给map
等,其中需要更高阶函数参数。
有关详细信息,请参阅http://www.braveclojure.com/writing-macros并搜索“Macros All Way Down”
请注意project.clj
需要
:dependencies [
[tupelo "0.9.13"]
让spyx
工作