在Clojure中计算传递给def的符号
我通过Clojure为勇敢和真诚的人工作。在有关宏的章节中,有以下练习: 编写一个宏,使用一个宏调用定义任意数量的属性检索函数。你可以这样称呼它:在Clojure中计算传递给def的符号,clojure,macros,Clojure,Macros,我通过Clojure为勇敢和真诚的人工作。在有关宏的章节中,有以下练习: 编写一个宏,使用一个宏调用定义任意数量的属性检索函数。你可以这样称呼它: (defattrs c-int :intelligence c-str :strength c-dex :dexterity) 这些函数所做的是从映射中检索值。例如:(定义字符{:name“Travis”,:intelligence 20,:strength 23,:dextrity 13}) (c-int字
(defattrs c-int :intelligence
c-str :strength
c-dex :dexterity)
这些函数所做的是从映射中检索值。例如:(定义字符{:name“Travis”,:intelligence 20,:strength 23,:dextrity 13})
(c-int字符)
的结果将是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:def的第一个参数必须是符号,compile:(/tmp/form-init566472754022288850.clj:2:1)
关于如何实现这一点,您有什么想法吗?您已经找到了后引号和波浪线的用例。试试这个:
(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表达式的实时代码。您已经找到了后引号和tilde的用例。试试这个:
(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表达式的实时代码。您可以对
属性执行一些普通操作,这些操作不需要作为表单生成:
- 将属性拆分为属性对;及
- 定义为每对生成
def
表单的函数
将以上内容应用到您的代码中,我们得到
(defmacro defattrs [& attributes]
(let [attribute-pairs (partition 2 attributes)]
(map (fn [[function-name attribute-key]]
`(def ~function-name #(~attribute-key %)))
attribute-pairs)))
- 后报价的范围仅限于我们希望生成的
def
- 函数的
函数名
和属性键
参数的值插入到def
表单中
还有一个问题
map
的结果是一系列def
表单
- 第一个将被解释为
适用于其他人
解决方案是将cons
ado
放在序列的前面:
(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
它起作用了 对于属性
参数,有一些普通操作不需要生成表单:
- 将属性拆分为属性对;及
- 定义为每对生成
def
表单的函数
将以上内容应用到您的代码中,我们得到
(defmacro defattrs [& attributes]
(let [attribute-pairs (partition 2 attributes)]
(map (fn [[function-name attribute-key]]
`(def ~function-name #(~attribute-key %)))
attribute-pairs)))
- 后报价的范围仅限于我们希望生成的
def
- 函数的
函数名
和属性键
参数的值插入到def
表单中
还有一个问题
map
的结果是一系列def
表单
- 第一个将被解释为
适用于其他人
解决方案是将cons
ado
放在序列的前面:
(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
它起作用了