如何使用defmacro而不是eval?

时间:2013-05-08 15:35:10

标签: macros clojure eval clojurescript

我已经提出了下面的函数,它按预期工作,但它使用了非常糟糕的eval,并且在我打算使用它的ClojureScript中不存在。

(defn path [d p]
  (eval 
    (concat '[-> d] 
      (flatten (map 
                 #(conj (repeat (dec %) 'z/right) 'z/down) 
                 (path-to-vector p))))))

如何将其转换为宏?我的尝试看起来像这样:

(defmacro path [d p]
  `(concat (-> ~d)
      (flatten
       (map #(conj (repeat (dec %) z/right) z/down)
             (path-to-vector ~p)))))

但这显然不起作用。

2 个答案:

答案 0 :(得分:4)

不需要宏或eval,操作只是reduce

(defn path [d p]
  (reduce (fn [s v]
            (reduce #(%2 %1) s (conj (repeat (dec v) z/right) z/down)))
          d (path-to-vector p)))

还要注意(conj (repeat (dec %) z/right) z/down)表示z / down然后所有z / right coz重复返回一个序列,如果你想要所有z / right第一个和最后一个项应该是z / down然后你应该使用{ {1}}

答案 1 :(得分:1)

Ankur是正确的,这是一个减少的情况,并且宏或eval都不合适,尽管它可能仍然值得解释写这样一个宏的机制,因为它本身就是这样:

您的宏示例非常接近,您只需要拼接 - 取消引用功能即可使其工作:

(defmacro path [d p]
  `(-> ~d 
      ~@(flatten
         (map #(conj (repeat (dec %) z/right) z/down)
               (path-to-vector ~p)))))

这会在宏扩展时评估调用以展平,然后将其连接到生成的s-expression /列表中。