2010-07-08 10 views

Répondre

144

Une fonction peut avoir plusieurs signatures si les signatures diffèrent en arité. Vous pouvez l'utiliser pour fournir des valeurs par défaut.

(defn string->integer 
    ([s] (string->integer s 10)) 
    ([s base] (Integer/parseInt s base))) 

Notez que l'hypothèse false et nil sont les non-valeurs considérées, (if (nil? base) 10 base) pourrait être raccourci à (if base base 10), ou encore à (or base 10).

+3

Je pense que ce serait mieux pour la deuxième ligne de dire '(RECUR s 10)', en utilisant [ 'recur'] (http: //grimoire.arrdem .com/1.6.0/clojure.core/recur /) au lieu de répéter le nom de la fonction 'string-> integer'. Cela rendrait plus facile de renommer la fonction à l'avenir. Est-ce que quelqu'un sait une raison de ne pas utiliser «se reproduire» dans ces situations? –

+9

Il semble que 'recur' ne fonctionne que sur la même arité. si vous avez essayé de revenir en arrière, par exemple: 'java.lang.IllegalArgumentException: nombre d'arguments incohérents à recréer, attendu: 1 args, obtenu: 2, compilation:' – djhaskin987

+0

Ran dans ce même problème. Serait-il logique d'avoir la fonction s'appelle elle-même (c'est-à-dire '(string-> integer s 10)')? –

140

Vous pouvez également déstructurer des arguments rest en tant que carte depuis Clojure 1.2 [ref]. Cela vous permet de nommer et de fournir les valeurs par défaut pour les arguments de la fonction:

(defn string->integer [s & {:keys [base] :or {base 10}}] 
    (Integer/parseInt s base)) 

Maintenant, vous pouvez appeler

(string->integer "11") 
=> 11 

ou

(string->integer "11" :base 8) 
=> 9 

Vous pouvez le voir en action ici: https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (par exemple)

+1

Si facile à comprendre si vous venez d'un arrière-plan Python :) – Dan

+1

C'est beaucoup plus facile à comprendre que la réponse acceptée ... est-ce le ["Clojurian"] accepté (https://github.com/bbatsov/clojure-style -guide) façon? S'il vous plaît envisager d'ajouter à ce document. – Droogans

+1

J'ai [ajouté un problème] (https://github.com/bbatsov/clojure-style-guide/issues/37) au guide de style non officiel pour aider à résoudre ce problème. – Droogans

31

Cette solution est la plus proche de la esprit de la solution d'origine, mais légèrement plus propre

(defn string->integer [str & [base]] 
    (Integer/parseInt str (or base 10))) 

Un modèle similaire qui peut être des utilisations pratiques or combinés avec let

(defn string->integer [str & [base]] 
    (let [base (or base 10)] 
    (Integer/parseInt str base))) 

Bien que dans ce cas, plus bavard, il peut être utile si vous souhaitez que les valeurs par défaut dépendent d'autres valeurs d'entrée. Par exemple, considérons la fonction suivante:

(defn exemplar [a & [b c]] 
    (let [b (or b 5) 
     c (or c (* 7 b))] 
    ;; or whatever yer actual code might be... 
    (println a b c))) 

(exemplar 3) => 3 5 35 

Cette approche peut facilement être étendue à travailler avec arguments nommés (comme dans la solution de M. Gilliar) ainsi:

(defn exemplar [a & {:keys [b c]}] 
    (let [b (or b 5) 
     c (or c (* 7 b))] 
    (println a b c))) 

Ou en utilisant encore plus d'une fusion:

(defn exemplar [a & {:keys [b c] :or {b 5}}] 
    (let [c (or c (* 7 b))] 
    (println a b c))) 
+0

Si vous n'avez pas besoin de vos valeurs par défaut dépendant d'autres valeurs par défaut (ou peut-être même si vous le faites), la solution de Matthew ci-dessus permet également plusieurs valeurs par défaut pour différentes variables. Il est beaucoup plus propre que d'utiliser un «ou» normal – johnbakers

+0

Je vais mettre à jour pour refléter cela. Merci. – metasoarous

+0

Je suis un Noo Clojure alors peut-être que OpenLearner a raison, mais c'est une alternative intéressante à la solution de Matthew ci-dessus. Je suis heureux de savoir si je décide finalement de l'utiliser ou non. – GlenPeterson

8

Il y a une autre approche que vous pouvez envisager: partial fonctions. Il s'agit sans doute d'une manière plus "fonctionnelle" et plus flexible de spécifier des valeurs par défaut pour les fonctions.

Commencez par créer (si nécessaire) une fonction qui a le paramètre (s) que vous souhaitez fournir par défaut (s) comme paramètre principal (s):

(defn string->integer [base str] 
    (Integer/parseInt str base)) 

Cela se fait parce que Clojure de La version de partial vous permet de fournir les valeurs "par défaut" uniquement dans l'ordre dans lequel elles apparaissent dans la définition de la fonction.Une fois que les paramètres ont été commandés comme vous le souhaitez, vous pouvez créer une version « par défaut » de la fonction en utilisant la fonction partial:

(partial string->integer 10) 

Afin de rendre cette fonction appelables plusieurs fois, vous pouvez le mettre dans un var en utilisant def:

(def decimal (partial string->integer 10)) 
(decimal "10") 
;10 

Vous pouvez également créer un "défaut locale" à l'aide let:

(let [hex (partial string->integer 16)] 
    (* (hex "FF") (hex "AA"))) 
;43350 

L'approche de la fonction partielle a un avantage clé sur les autres: le consommateur de la fonction peut encore décider quelle sera la valeur par défaut plutôt que le producteur de la fonction sans avoir besoin de modifier la définition de la fonction. Ceci est illustré dans l'exemple avec hex où j'ai décidé que la fonction par défaut decimal n'est pas ce que je veux. Un autre avantage de cette approche est que vous pouvez affecter à la fonction par défaut un nom différent (décimal, hexadécimal, etc.) qui peut être plus descriptif et/ou une portée différente (var, local). La fonction partielle peut aussi être mélangé avec certaines des approches ci-dessus si on le souhaite:

(defn string->integer 
    ([s] (string->integer s 10)) 
    ([base s] (Integer/parseInt s base))) 

(def hex (partial string->integer 16)) 

(Notez que c'est légèrement différente de la réponse de Brian comme l'ordre des paramètres a été inversée pour les raisons indiquées en haut de cette réponse)

+1

Ce n'est pas ce que la question demande; c'est intéressant cependant. – Zaz