我想要一个我称之为def-foo的宏。 Def-foo将创建一个函数,然后将此函数添加到集合中。
所以我可以打电话给
(def-foo bar ...)
(def-foo baz ...)
然后会有一些设置,例如all-foos,我可以称之为:
all-foos
=> #{bar, baz}
基本上,我只是想避免重复自己。我当然可以用正常方式定义函数,(defn bar ...)然后手动编写集合。
一个更好的选择,比宏观思想更简单,就是:
(def foos #{(defn bar ...) (defn baz ...)} )
但我仍然很好奇是否有一个很好的方式让宏观思想发挥作用。
答案 0 :(得分:5)
让宏在创建函数之前将新函数的名称添加到集合中,如下所示:
(def *foos* (atom (hash-set)))
(defmacro def-foo [name]
(swap! *foos* conj name)
`(defn ~name
[]
(println "This is a foo!")))
结果:
user=> (def-foo bar)
#'user/bar
user=> (def-foo baz)
#'user/baz
user=> (bar)
This is a foo!
nil
user=> (baz)
This is a foo!
nil
user=> *foos*
#<Atom@186b11c: #{baz bar}>
答案 1 :(得分:5)
你想最终得到一个具有其中的函数名称的集合(即一组符号),或者包含变量的集合(解析为函数)?如果你想要前者,可以在编译时将符号添加到宏中的原子,就像Greg Harman的版本一样。
如果你想要后者,你的宏必须扩展到定义函数后进行原子交换的代码。请记住,宏在编译时运行,宏扩展结果在运行时运行;函数本身在运行时才可用。
(def all-foos (atom #{}))
(defmacro def-foo [x]
`(let [var# (defn ~x [] (println "I am" '~x))]
(swap! all-foos conj var#)))
例如,如果您希望能够调用此集合中的函数,则需要使用后一版本。
user> (def-foo foo)
#{#'user/foo}
user> (def-foo bar)
#{#'user/foo #'user/bar}
user> ((first @all-foos))
I am foo
nil