【问题标题】:How to use defmacro instead of eval?如何使用 defmacro 而不是 eval?
【发布时间】:2013-05-08 15:35:10
【问题描述】:

我想出了下面的函数,它按预期工作,但它使用了 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)))))

但这显然行不通。

【问题讨论】:

  • 首先,不需要在语法引用中使用 concat。你的宏可能看起来像:`(-> d ~@(flatten ...))
  • 您的宏将取决于它的使用情况,d 和 p 值是在编译时可用还是在运行时可用?

标签: macros clojure eval clojurescript


【解决方案1】:

不需要宏或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,那么您应该使用 (conj (vec (repeat (dec %)) z/right) z/down)

【讨论】:

    【解决方案2】:

    Ankur 是正确的,这是 reduce 的情况,宏或 eval 都不合适,尽管为了它自己的缘故,可能仍然值得解释编写这样的宏的机制:

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

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

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

    【讨论】:

    • 你不需要 concat,只需要 `(-> ~d ~@(flatten ...))
    • 这是真的 :) 我试图尽可能接近原作。我会解决的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-10
    • 1970-01-01
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多