评估在Clojure中传递给def的符号

时间:2017-02-23 15:45:06

标签: clojure macros

我正在通过Clojure为Brave和True工作。在关于宏的章节中,有一个练习:

编写一个宏,使用一次宏调用定义任意数量的属性检索函数。以下是您的称呼方式:

(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)

关于如何实现这一目标的任何想法?

2 个答案:

答案 0 :(得分:2)

您已找到反引号和波浪号的用例。试试这个:

(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

反引号类似于单引号,因为它使下一个表单成为列表,符号等的数据结构。不同之处在于数据结构旨在用作模板,其中内部可以使用代字号替换位。很酷的部分是代字号并不仅仅替换项目,而是适用于可以是任意Clojure表达式的实时代码。

答案 1 :(得分:1)

您使用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-nameattribute-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

有效。