Macros 如何将可选关键字参数与&;其他的东西?
我有一个取实体的宏:Macros 如何将可选关键字参数与&;其他的东西?,macros,clojure,destructuring,Macros,Clojure,Destructuring,我有一个取实体的宏: (defmacro blah [& body] (dostuffwithbody)) 但我还想给它添加一个可选的关键字参数,因此当调用它时,它可能看起来像以下任何一个: (blah :specialthingy 0 body morebody lotsofbody) (blah body morebody lotsofboy) 我该怎么做?注意,我使用的是Clojure1.2,所以我还使用了新的可选关键字参数destructuring。我天真地尝试这样做: (d
(defmacro blah [& body] (dostuffwithbody))
但我还想给它添加一个可选的关键字参数,因此当调用它时,它可能看起来像以下任何一个:
(blah :specialthingy 0 body morebody lotsofbody)
(blah body morebody lotsofboy)
我该怎么做?注意,我使用的是Clojure1.2,所以我还使用了新的可选关键字参数destructuring。我天真地尝试这样做:
(defmacro blah [& {specialthingy :specialthingy} & body])
但很明显,这并不奏效。如何实现这一点或类似的功能?MichałMarczyk很好地回答了您的问题,但是如果您愿意接受(foo{}其余的参数),那么使用可选关键字作为第一个参数的映射(在本例中为空),那么这个更简单的代码就可以了:
(defn foo [{:keys [a b]} & rest] (list a b rest))
(foo {} 2 3)
=> (nil nil (2 3))
(foo {:a 1} 2 3)
=> (1 nil (2 3))
对defmacro
也适用。这样做的好处是,您不必记住可选关键字参数的特殊语法,也不必实现处理特殊语法的代码(可能其他调用宏的人更习惯于在映射中指定键值对,而不是在映射之外)。当然,缺点是您必须始终提供映射,即使您不想提供任何关键字参数
当您想要消除此缺点时,一个小的改进是检查是否提供了映射作为参数列表的第一个元素:
(defn foo [& rest]
(letfn [(inner-foo [{:keys [a b]} & rest] (list a b rest))]
(if (map? (first rest))
(apply inner-foo rest)
(apply inner-foo {} rest))))
(foo 1 2 3)
=> (nil nil (1 2 3))
(foo {:a 2 :b 3} 4 5 6)
=> (2 3 (4 5 6))
类似于以下内容(还可以参见
defn
和clojure.core
中的一些其他宏是如何定义的):
或者你可以更老练一些;如果您认为您可能希望在某个时候拥有多个可能的选项,那么这可能是值得的:
(defn foo [& args]
(let [aps (partition-all 2 args)
[opts-and-vals ps] (split-with #(keyword? (first %)) aps)
options (into {} (map vec opts-and-vals))
positionals (reduce into [] ps)]
[options positionals]))
(foo :a 1 :b 2 3 4 5)
; => [{:a 1, :b 2} [3 4 5]]
(defn foo [& args]
(let [aps (partition-all 2 args)
[opts-and-vals ps] (split-with #(keyword? (first %)) aps)
options (into {} (map vec opts-and-vals))
positionals (reduce into [] ps)]
[options positionals]))
(foo :a 1 :b 2 3 4 5)
; => [{:a 1, :b 2} [3 4 5]]