7

私は体を取るマクロを持っています:

(defmacro blah [& body] (dostuffwithbody))

ただし、オプションのキーワード引数も追加したいので、呼び出されると、次のいずれかのようになります。

(blah :specialthingy 0 body morebody lotsofbody)
(blah body morebody lotsofboy)

どうやってやるの?私はClojure1.2を使用しているので、新しいオプションのキーワード引数を破壊するものも使用していることに注意してください。私は素朴にこれをやろうとしました:

(defmacro blah [& {specialthingy :specialthingy} & body])

しかし、明らかにそれはうまくいきませんでした。どうすればこれまたは同様のことを達成できますか?

4

2 に答える 2

9

次のようなものかもしれません(どのようdefnに、そして他のいくつかのマクロclojure.coreが定義されているかも見てください):

(defmacro blah [& maybe-option-and-body]
  (let [has-option (= :specialthingy (first maybe-option-and-body))
        option-val (if has-option (second maybe-option-and-body)) ; nil otherwise
        body (if has-option (nnext maybe-option-and-body) maybe-option-and-body)]
    ...))

あるいは、もっと洗練されているかもしれません。ある時点で複数の可能なオプションが必要になると思われる場合は、価値があるかもしれません。

(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]]
于 2010-05-02T15:57:38.300 に答える
3

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))
于 2010-05-02T15:57:17.990 に答える