假设我们在任意位置都有一个(a b c _ e f)
的列表。例如:_
。我正在尝试编写一个宏,对于这样的列表,它会找到z
并将其替换为另一个值(例如(a b c z e f)
):bin/felix.jar
。
最佳方法是什么?
答案 0 :(得分:6)
你确定需要一个宏吗?只要引用列表,使用replace就可以正常工作:
(replace '{_ z} '(a b c _ e f)) ; => (a b c z e f)
答案 1 :(得分:1)
_
值(可能是为了记录),要像这样使用它:
(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)))
这正是我们想要的。