Macros 无parens的反向报价

Macros 无parens的反向报价,macros,clojure,common-lisp,Macros,Clojure,Common Lisp,我正在阅读优秀的书Let Over Lambda,并尝试将defunits的公共Lisp代码移植到Clojure 下面生成一个宏,该宏应该 (defn defunits-chaining [u units prev] (if (some #(= u %) prev) (throw (Throwable. (str u " depends on " prev)))) (let [spec (first (filter #(= u (first %)) units))] (i

我正在阅读优秀的书Let Over Lambda,并尝试将defunits的公共Lisp代码移植到Clojure

下面生成一个宏,该宏应该

(defn defunits-chaining [u units prev]
  (if (some #(= u %) prev)
    (throw (Throwable. (str u " depends on " prev))))
  (let [spec (first (filter #(= u (first %)) units))]
    (if (nil? spec)
      (throw (Throwable. (str "unknown unit " u)))
      (let [chain (second spec)]
        (if (list? chain)
          (* (first chain)
             (defunits-chaining
               (second chain)
               units
               (cons u prev)))
          chain)))))

(defmacro defunits [quantity base-unit & units]
  `(defmacro ~(symbol (str "unit-of-" quantity))
     [valu# un#]
     `(* ~valu#
         ~(case un#
           ~base-unit 1
            ~@(map (fn [x]
                     `(~(first x)               ;; <-- PRETTY SURE IT'S THIS `(
                       ~(defunits-chaining
                          (first x)
                          (cons `(~base-unit 1)
                                (partition 2 units))
                          nil)))
                   (partition 2 units))))))

(defunits time s m 60 h 3600)
并以基本单位给出结果(本例中为秒)。我认为问题在于“case”中Clojure/CL api的变化。CL中的“Case”如下所示:

(case 'a (('b) 'no) (('c) 'nope) (('a) 'yes))
但是在clojure

(case 'a 'b 'no 'c 'nope 'a 'yes)
多方便啊。我在嵌套的defmacro中更改了我的anon函数,但它一直生成

(case un#
  s 1
  (m 60)
  (h 3600)

如何防止这些外部排列?

您可以连接括号中的对(例如使用
应用concat
)或使用amalloy的答案(最好的主意)

您还可以使用seq函数构造您的返回表单:

(defmacro defunits [...]
  (let [macro-name (symbol ...)
        valsym (gensym "val__")
        unitsym (gensym "unit__")]
    (list `defmacro             ; using ` so the symbol gets ns-qualified
          macro-name
          [valsym unitsym]
          (make-body-expression ...)))) ; define appropriately

我认为这在这里是有意义的,因为不管怎样,你最终几乎把所有的东西都取消了引号,有两个级别的语法引号;不过,这在很大程度上取决于你的品味。

如果你把地图放平,它应该会产生你想要的结果

(defmacro defunits [quantity base-unit & units]
  `(defmacro ~(symbol (str "unit-of-" quantity))
     [valu# un#]
     `(* ~valu#
         ~(case un#
           ~base-unit 1
            ~@(flatten ;; <- changed this
                (map (fn [x]
                       `(~(first x)
                         ~(defunits-chaining
                            (first x)
                            (cons `(~base-unit 1)
                                  (partition 2 units))
                            nil)))
                   (partition 2 units)))))))
(定义宏定义单位[数量基本单位和单位]
`(defmacro~(symbol(str“数量单位”)
[valu#un#]
`(*瓦卢)#
~(案例联合国)#
~1号基地

~@(展平;;只需将
map
更改为
mapcat

(defmacro defunits [quantity base-unit & units]
  `(defmacro ~(symbol (str "unit-of-" quantity))
     [valu# un#]
     `(* ~valu#
         ~(case un#
            ~base-unit 1
            ~@(mapcat (fn [x] ;; <----- mapcat instead of map is the only change
                        `(~(first x)
                          ~(defunits-chaining
                             (first x)
                             (cons `(~base-unit 1)
                                   (partition 2 units))
                             nil)))
                      (partition 2 units))))))
(定义宏定义单位[数量基本单位和单位]
`(defmacro~(symbol(str“数量单位”)
[valu#un#]
`(*瓦卢)#
~(案例联合国)#
~1号基地

~@(mapcat(fn[x];;哇,非常感谢,这将花费我一两天的时间来解决。我不知道你可以从一个seq构造它,这真的很酷。这是经过深思熟虑的,非常感谢。宏只是适当地注册了(在编译器中)返回列表结构的函数。带取消引号的反引号/语法引号只是生成列表结构的一种可能方法。每种方法都可以使用(而且很有用),而不必使用其他方法(尽管我相信Clojure,但我发现自己在宏外使用语法引号的频率比在Scheme中使用反引号的频率要低).编辑以表明我认为amalloy的答案是最值得使用的绿色勾号。我仍然更喜欢展开一级语法引用,因此我将保留此答案以供参考。是的,~@(展平(地图)(fn[x]做得很好,我不确定在我尝试的100件事情中我怎么会错过它。头部撞击这正是我之前想要的。我想我需要在clojure标准函数中做一些挖掘。是的,再看看这个问题,这是正确的答案。
(defmacro defunits [quantity base-unit & units]
  `(defmacro ~(symbol (str "my-unit-of-" quantity))
     [valu# un#]
     `(* ~valu#
         ~(case un#
           ~base-unit 1
            ~@(reduce (fn [x y] ;; <- reduce instead of map
                        (concat x ;; <- use concat to string the values together
                              `(~(first y)
                                ~(defunits-chaining
                                   (first y)
                                   (cons `(~base-unit 1)
                                         (partition 2 units))
                                   nil))))
                      '()
                      (partition 2 units))))))
(defmacro defunits [quantity base-unit & units]
  `(defmacro ~(symbol (str "unit-of-" quantity))
     [valu# un#]
     `(* ~valu#
         ~(case un#
            ~base-unit 1
            ~@(mapcat (fn [x] ;; <----- mapcat instead of map is the only change
                        `(~(first x)
                          ~(defunits-chaining
                             (first x)
                             (cons `(~base-unit 1)
                                   (partition 2 units))
                             nil)))
                      (partition 2 units))))))
(defmacro defunits [quantity base-unit & units]
  `(defmacro ~(symbol (str "unit-of-" quantity))
     [valu# un#]
     `(* ~valu#
         ~(case un#
            ~base-unit 1
            ~@(mapcat (fn [x] ;; <----- mapcat instead of map is the only change
                        `(~(first x)
                          ~(defunits-chaining
                             (first x)
                             (cons `(~base-unit 1)
                                   (partition 2 units))
                             nil)))
                      (partition 2 units))))))