写一个替换宏的参数

时间:2016-02-02 01:55:41

标签: clojure clojurescript

假设我们在任意位置都有一个(a b c _ e f)的列表。例如:_。我正在尝试编写一个宏,对于这样的列表,它会找到z并将其替换为另一个值(例如(a b c z e f)):bin/felix.jar

最佳方法是什么?

2 个答案:

答案 0 :(得分:6)

你确定需要一个宏吗?只要引用列表,使用replace就可以正常工作:

(replace '{_ z} '(a b c _ e f)) ; =>  (a b c z e f)

答案 1 :(得分:1)

@Josh的答案是对的,首先你应该决定你是否真的需要宏来做某事。我可以想象一个合成的例子。让我们假装你想要定义一个函数,但是由于某种原因需要跟踪这个_值(可能是为了记录),要像这样使用它:

(defn-with-placeholder my-fn [a b c _ e] z
  (println a b c z e))

你是怎么做到的:

(defmacro defn-with-placeholder [name args placeholder & body]
  `(defn ~name ~(vec (replace {'_ placeholder} args))
     ~@body))

请注意,我之前提出过使用相同的replace方法。

让我们在repl中进行测试:

user> (defn-with-placeholder my-fn [a b _ d] placeholder 
        (println a b placeholder d))
#'user/my-fn
user> (my-fn 1 2 3 4)
1 2 3 4
nil
好吧,现在它很无用了。让我们进一步练习,并做一个定义,将所有省略的参数收集到一些集合(如函数休息参数& args,但在不同的位置) 所以我们可以定义一个像这样工作的宏defn-with-omitted

(defn-with-omitted my-fn-2 [a _ c _ e f _ h] other-args
  (println :parameters a c e f h)
  (println :other-parameters other-args))

在repl中:

user> (my-fn-2 1 100 2 200 3 4 300 5)
:parameters 1 2 3 4 5
:other-parameters {1 100, 3 200, 6 300}
nil

它收集所有省略的数据并将其放到other-args地图中,并使用arg-position进行arg映射。

首先,我们需要创建一个处理arglist并收集所有省略的参数的函数:

(defn process-args [args]
  (reduce-kv (fn [[args omitted] idx arg]
               (if (= '_ arg)
                 (let [sym-name (gensym "omitted")]
                   [(conj args sym-name)
                    (assoc omitted idx sym-name)])
                 [(conj args arg) omitted]))
             [[] {}]
             args))

这是它的作用:

user> (process-args '[a _ b c _ _ f g])
[[a omitted29608 b c omitted29609 omitted29610 f g] 
 {1 omitted29608, 4 omitted29609, 5 omitted29610}]

请注意我在这里使用了gensym,而不是影响可能的外部定义。

所以现在制作宏非常容易:

(defmacro defn-with-omitted [name args omitted-name & body]
  (let [[args omitted] (process-args args)]
    `(defn ~name ~args
       (let [~omitted-name ~omitted]
         ~@body))))

让我们检查扩展:

(defn-with-omitted my-fn-2 [a _ c _ e f _ h] other-args
  (println :parameters a c e f h)
  (println :other-parameters other-args))

扩展为:

(defn my-fn-2 [a omitted29623 c omitted29624 e f omitted29625 h]
  (let [other-args {1 omitted29623, 3 omitted29624, 6 omitted29625}]
    (println :parameters a c e f h)
    (println :other-parameters other-args)))

这正是我们想要的。