我正在为勇敢和真而努力。在关于宏的一章中,有这样的练习:
编写一个宏,该宏使用一个宏调用定义任意数量的属性检索函数。你可以这样称呼它:
(defattrs c-int :intelligence
c-str :strength
c-dex :dexterity)这些函数所做的是从映射中检索值。例如:给定:(def character {:name "Travis", :intelligence 20, :strength 23, :dexterity 13})
(c-int character)的结果是20,当然,这样的函数可以很容易地定义为(def c-int #(:intelligence %))。
这就是我想出的解决问题的方法:
(defmacro defattrs
[& attributes]
`(let [attribute-pairs# (partition 2 (quote ~attributes))]
(map (fn [[function-name# attribute-key#]]
(def function-name# #(attribute-key# %)))
attribute-pairs#)))我遇到的问题是,def使用生成的符号名,而不是它决定定义函数的内容(事后看来,考虑到def的使用,这是有意义的)。我尝试在定义函数时使用表达式,例如:
(let [x ['c-int :intelligence]]
(def (first x) #((second x) %)))已导致此错误:CompilerException java.lang.RuntimeException: First argument to def must be a Symbol, compiling:(/tmp/form-init5664727540242288850.clj:2:1)
对我如何做到这一点有什么想法吗?
发布于 2017-02-23 18:54:17
对于不需要以表单形式生成的attributes参数,有一些常见的操作:
def表单。把上面的代码应用到你的代码中,我们得到.
(defmacro defattrs [& attributes]
(let [attribute-pairs (partition 2 attributes)]
(map (fn [[function-name attribute-key]]
`(def ~function-name #(~attribute-key %)))
attribute-pairs)))def。function-name和attribute-key参数的值被插入到def表单中。还有一个问题。
map的结果是一个def表单序列。解决方案是将一个cons do放在序列的前面:
(defmacro defattrs [& attributes]
(let [attribute-pairs (partition 2 attributes)]
(cons 'do
(map (fn [[function-name attribute-key]]
`(def ~function-name ~attribute-key))
attribute-pairs))))我还在后面引用的表单中将#(~attribute-key %)缩写为等效的~attribute-key。
让我们看看扩展是什么样子的:
(macroexpand-1 '(defattrs dooby :brrr))
;(do (def dooby :brrr))看起来不错。我们试试看!
(defattrs gosh :brrr)
(gosh {:brrr 777})
;777它起作用了。
发布于 2017-02-23 16:48:35
你已经找到了回溯和倾斜的用例。试试看:
(let [x ['c-int :intelligence]]
(eval `(def ~(first x) #(~(second x) %))))
(def character {:name "Travis", :intelligence 20, :strength 23, :dexterity 13})
(c-int character) => 20后引号类似于单引号,因为它将下一个格式变成列表、符号等的数据结构。不同的是,数据结构被用作模板,其中内部位可以用倾斜体替换。最酷的部分是,tilde不仅可以替代项,而且可以用于任何任意Clojure表达式的活动代码。
https://stackoverflow.com/questions/42420250
复制相似问题