2017-02-23 46 views
0

我正在通过Clojure的勇敢和真实。在宏的章节中有这样的练习:评估在Clojure中传递给def的符号

使用一个宏调用编写一个定义任意数量的属性检索函数的宏。你可以这样称呼它:

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

我如何能做到这一点任何想法?

回答

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表单。
  • 第一个将被解释为功能 适用于其余。

的解决方案是一个consdo到序列的前:

(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 

它的工作原理。

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表达式的实时代码。