2010-11-26 7 views
11

Ich möchte eine Funktion erstellen, die ein erforderliches Argument x und ein optionales Argument opt1 ODER ein Schlüsselwort argument opt2 einnimmt.Clojure-Schlüsselwort und optionales Argument Problem

Im Moment habe ich

(defn foo x & [opt1 {:keys [opt2]}] 
    ... 

Aber die oben Unterschrift lässt mich nur in Keyword-Argument opt2 passieren, wenn beide x und opt1 vorhanden ist wie

(foo 'x 'opt1 {:opt2 'opt2}) 

Nicht so

(foo 'x {:opt2 'opt2}) 

Bitte helfen Sie mir eine Funktion zu erstellen, die ein erforderliches Argument X und entweder opt1 oder opt2, whe re opt2 ist ein Schlüsselwort-Argument.

Vielen Dank.

EDIT: Ich möchte das gleiche auch für andere Makros tun. Also muss ich immer noch den defmacro benutzen.

+0

Betrachten wir mit 'defnk' von [clojure.contrib.def] (http://richhickey.github.com/clojure-contrib/def-api.html) anstelle von expliziten Destrukturierung. – ffriend

+0

'defnk' ist ab 1.2 zugunsten einer konsistenteren integrierten Funktionalität veraltet. – kotarak

Antwort

15

Das Problem ist Mehrdeutigkeit. Stellen Sie sich eine Funktion (fn foo [x y & args]) vor, die zwei optionale Argumente und dann eine beliebige Anzahl von Schlüsselwortargumenten benötigt. Wenn Sie es dann wie (foo :bar :baz) nennen, wie geht Ihr Programm damit um? x =>:bar, y =>:baz? Oder x und y nicht zur Verfügung gestellt, mit einem einzigen :bar =>:baz Schlüsselwort Argument?

Auch in Common Lisp, das in Parsing-Funktionsparametern wohl noch mehr Flexibilität als Clojure bietet, wird das Mischen von optionalen und Schlüsselwortargumenten nach mindestens one popular book nicht empfohlen.

Am besten ändern Sie alle Argumente in Positionsargumente oder alle Parameter in Schlüsselwortargumente. Wenn Sie Schlüsselwortargumente verwenden, können Sie Hash-Map-Destrukturierung verwenden, um Standardwerte für "optionale" Schlüsselwortparameter bereitzustellen.

user> (defn foo [& {:keys [x y bar] 
        :or {x 1 y 2 bar 3}}] 
     (prn [x y bar])) 
#'user/foo 
user> (foo) 
[1 2 3] 
nil 
user> (foo :bar :baz) 
[1 2 :baz] 
nil 
2

Sie müssen überprüfen, ob die aditional Argumente Schlüsselwort Argumente sind oder nicht sowieso (ich nehme an, Ihr oder ein exklusives oder), so können Sie es wie folgt tun:

(defn foo [& args] 
    (if (= (count args) 1) 
     (let [[opt1] args] (println opt1)) 
     (let [{:keys [opt2]} args] (println opt2)))) 

Prüfung, welche die Argumente, wenn sie Schlüsselwort sind Argumente oder nicht. Da Sie nur einen optionalen Parameter haben, ist es ganz einfach: Überprüfen Sie, ob es nur einen gibt, da Schlüsselwortargumente zwei erfordern.