2010-11-26 41 views
11

Je souhaite créer une fonction qui accepte un argument x obligatoire et un argument optionnel opt1 OU un argument mot-clé opt2.Mot-clé Clojure et problème d'argument facultatif

En ce moment, j'ai

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

Mais la signature ci-dessus me ne laisse passer que dans l'argument mot-clé opt2 lorsque x et OPT1 est présent comme

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

pas comme ça

(foo 'x {:opt2 'opt2}) 

S'il vous plaît aidez-moi à créer une fonction qui prend un argument requis X et soit opt1 ou opt2, whe re opt2 est un argument mot-clé.

Merci.

EDIT: Je veux faire de même pour les autres macros. Donc j'ai encore besoin d'utiliser le defmacro.

+0

Utiliser 'defnk' de [clojure.contrib.def] (http://richhickey.github.com/clojure-contrib/def-api.html) au lieu de déstructuration explicite. – ffriend

+0

'defnk' est déprécié au profit d'une fonctionnalité intégrée plus cohérente à partir de la version 1.2. – kotarak

Répondre

15

Le problème est l'ambiguïté. Considérez une fonction (fn foo [x y & args]) qui prend deux arguments facultatifs, puis n'importe quel nombre d'arguments de mot-clé. Si vous l'appelez ensuite comme (foo :bar :baz), comment votre programme le gère-t-il? x =>:bar, y =>:baz? Ou x et y pas fourni, avec un seul argument mot-clé :bar =>:baz?

Même en Common Lisp, qui a sans doute encore plus de flexibilité que Clojure dans les paramètres de la fonction d'analyse, le mélange d'arguments optionnels et de mots-clés n'est pas recommandé, selon au moins one popular book. Votre meilleur pari est de changer tous vos arguments en arguments de position, ou tous vos paramètres en arguments de mots-clés. Si vous utilisez des arguments de mots-clés, vous pouvez utiliser la déstructuration de hachage pour fournir des valeurs par défaut pour les paramètres de mot-clé "facultatif".

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

vous devez vérifier si les arguments aditional sont des arguments de mots clés ou pas de toute façon (je suppose que votre ou est un ou exclusif) donc vous pouvez le faire comme ceci:

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

contrôle les arguments si elles sont mot-clé arguments ou non. Comme vous n'avez qu'un seul paramètre optionnel, c'est simple: vérifiez s'il n'y en a qu'un seul car les arguments du mot-clé en nécessitent deux.