1

我是使用 Light Table 学习宏的 Clojure 新手。我的目标是将映射向量转换为 def 语句列表。

我想转换以下数据结构:

(def label-data
  [
   {:label "lbl_first"}
   {:label "lbl_second"}
   {:label "lbl_third"}
   {:label "lbl_fourth"}
  ]
)

...进入以下 def 语句:

  (def L1 {:label "lbl_first"})
  (def L2 {:label "lbl_second"})
  (def L3 {:label "lbl_third"})
  (def L4 {:label "lbl_fourth"})

我知道如何创建一个生成单个 def 语句的宏:

(defmacro def-label [num]
  (let [ idx (dec num)
         symb (symbol (str "L" idx))
         datum (label-data num)
         syntax `(def ~symb ~datum)]
    syntax))

当我使用宏...

(def-label 2)

我可以看到宏生成的符号成功解析为...

L2

现在,我可以概念化创建一个如下所示的宏:

 (defmacro generate-def-statements-from [lbldata]

 )

但我不明白如何迭代 def-label 宏 4 次以生成所需的多个 def 语句。有人可以向我展示实现这一目标的最佳技术吗?

事先感谢您的建议和指导。

4

2 回答 2

2

将一个表达式转换为另一个表达式,因此您需要生成一个表达式。这可以通过将defs 包装在do

user> (defmacro def-label-data [label-data]
          `(do ~@(map #(list 'def %1 %2) 
                      (map #(symbol (str "L" %))
                                    (range 1 (inc (count label-data))))
                      label-data)))
#'user/def-label-data

user> (macroexpand '(def-label-data [{:label "lbl_first"}
                                     {:label "lbl_second"}
                                     {:label "lbl_third"}
                                     {:label "lbl_fourth"}]))
(do (def L1 {:label "lbl_first"})
    (def L2 {:label "lbl_second"})
    (def L3 {:label "lbl_third"})
    (def L4 {:label "lbl_fourth"}))

这可能不是来自许多其他语言的直觉,在这些语言中定义新的顶级表单只能在顶级发生。在clojure中,情况并非如此。您可以在任何级别调用 def,即使是另一种形式。请记住,它会产生一个顶级 var。

如果您想将其作为函数而不是宏来执行,则不能使用def,因为它是一种特殊形式,将其第一个参数视为符号而不对其进行评估。幸运intern的是,它与 def 做同样的事情,除了它评估它的论点:

user> (defn def-label-data [label-data]  
         (map #(intern *ns* %1 %2) 
              (map #(symbol (str "L" %)) 
                   (range 1 (inc (count label-data)))) 
              label-data))
#'user/def-label-data

user> (def-label-data [{:label "lbl_first"}
                       {:label "lbl_second"}
                       {:label "lbl_third"}
                       {:label "lbl_fourth"} 
                       {:label "lbl_fifth"}])
(#'user/L1 #'user/L2 #'user/L3 #'user/L4 #'user/L5)
user> L5
{:label "lbl_fifth"}
user> L4
{:label "lbl_fourth"}
user> L3
{:label "lbl_third"}
user> L2
{:label "lbl_second"}
user> L1
{:label "lbl_first"} 
于 2013-05-09T21:48:31.073 回答
1

  1. 不要评估他们的论点
  2. 评估它们的返回值

所以,

 (defmacro generate-def-statements-from [lbldata] 
  `(do ~@(map-indexed 
           (fn [idx itm] `(def ~(symbol (str "L" (inc idx))) ~itm)) 
           lbldata)))

必须处理文字表达式

(macroexpand 
  '(generate-def-statements-from   
    [ {:label "lbl_first"}
      {:label "lbl_second"}
      {:label "lbl_third"}
      {:label "lbl_fourth"} ]))

;=> (do
;     (def L1 {:label "lbl_first"})
;     (def L2 {:label "lbl_second"})
;     (def L3 {:label "lbl_third"})
;     (def L4 {:label "lbl_fourth"}))

并将评估do表格以def符合标签。

于 2013-05-09T21:43:05.150 回答