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
,如何实现这一点?