Clojure 编写替换宏的参数
假设我们有一个列表,在任意位置有一个Clojure 编写替换宏的参数,clojure,clojurescript,Clojure,Clojurescript,假设我们有一个列表,在任意位置有一个。。例如:(abc\ef)。我正在尝试编写一个宏,对于这样的列表,它将查找。,并用另一个值替换它(例如,z):(a b c z e f) 最好的方法是什么?您确定需要宏吗?只要您引用以下列表,使用就可以了: (replace '{_ z} '(a b c _ e f)) ; => (a b c z e f) @Josh的答案是正确的,首先你应该决定你是否真的需要宏。我可以想象一个综合的例子。让我们假设您想定义一个函数,但出于某种原因(可能是为了日志记
。
。例如:(abc\ef)
。我正在尝试编写一个宏,对于这样的列表,它将查找。
,并用另一个值替换它(例如,z
):(a b c z e f)
最好的方法是什么?您确定需要宏吗?只要您引用以下列表,使用就可以了:
(replace '{_ z} '(a b c _ e f)) ; => (a b c z e f)
@Josh的答案是正确的,首先你应该决定你是否真的需要宏。我可以想象一个综合的例子。让我们假设您想定义一个函数,但出于某种原因(可能是为了日志记录),需要跟踪该
\uu
值,以便像这样使用它:
(defn-with-placeholder my-fn [a b c _ e] z
(println a b c z e))
(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))
你就是这样做的:
(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
好了,现在它已经没用了。让我们继续练习,并做一个定义,将所有省略的参数收集到某个集合中(如函数rest parameters&args
,但位于不同的位置)
因此,我们可以定义一个省略了的宏defn,其工作原理如下:
(defn-with-placeholder my-fn [a b c _ e] z
(println a b c z e))
(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))
答复:
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
它收集所有省略的数据,并将其放入其他arg
映射,并将arg位置映射到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)))
这正是我们想要的。这很优雅,但我的问题错了:我需要的不是返回列表,而是替换列表,然后进行评估。在ClojureScript中,如果没有eval
,如何实现这一点?