Risposte:
Una funzione può avere più firme se le firme differiscono per arità. Puoi usarlo per fornire valori predefiniti.
(defn string->integer
([s] (string->integer s 10))
([s base] (Integer/parseInt s base)))
Si noti che assumendo falsee nilsono entrambi considerati non valori, (if (nil? base) 10 base)potrebbe essere abbreviato (if base base 10)o ulteriormente (or base 10).
recurfunzioni solo sulla stessa arità. se hai provato a ripetere in precedenza, ad esempio:java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
(string->integer s 10))?
Puoi anche distruggere gli restargomenti come una mappa da Clojure 1.2 [ ref ]. Ciò consente di nominare e fornire i valori predefiniti per gli argomenti delle funzioni:
(defn string->integer [s & {:keys [base] :or {base 10}}]
(Integer/parseInt s base))
Adesso puoi chiamare
(string->integer "11")
=> 11
o
(string->integer "11" :base 8)
=> 9
Puoi vederlo in azione qui: https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (ad esempio)
Questa soluzione è la più vicina allo spirito della soluzione originale , ma leggermente più pulita
(defn string->integer [str & [base]]
(Integer/parseInt str (or base 10)))
Un modello simile che può essere utile utilizza orcombinato conlet
(defn string->integer [str & [base]]
(let [base (or base 10)]
(Integer/parseInt str base)))
Mentre in questo caso è più dettagliato, può essere utile se si desidera che le impostazioni predefinite dipendano da altri valori di input . Ad esempio, considerare la seguente funzione:
(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
Questo approccio può essere facilmente esteso per lavorare con argomenti denominati (come nella soluzione di M. Gilliar):
(defn exemplar [a & {:keys [b c]}]
(let [b (or b 5)
c (or c (* 7 b))]
(println a b c)))
O usando ancora più di una fusione:
(defn exemplar [a & {:keys [b c] :or {b 5}}]
(let [c (or c (* 7 b))]
(println a b c)))
or
orè diverso da :orpoiché ornon conosce la differenza di nile false.
C'è un altro approccio che potresti voler prendere in considerazione: funzioni parziali . Questi sono probabilmente un modo più "funzionale" e più flessibile per specificare valori predefiniti per le funzioni.
Inizia creando (se necessario) una funzione che abbia i parametri che desideri fornire come parametri predefiniti come i parametri principali:
(defn string->integer [base str]
(Integer/parseInt str base))
Questo perché la versione di Clojure di partialconsente di fornire i valori "predefiniti" solo nell'ordine in cui compaiono nella definizione della funzione. Una volta che i parametri sono stati ordinati come desiderato, è quindi possibile creare una versione "predefinita" della funzione utilizzando la partialfunzione:
(partial string->integer 10)
Per rendere questa funzione richiamabile più volte è possibile inserirla in una var usando def:
(def decimal (partial string->integer 10))
(decimal "10")
;10
Puoi anche creare un "default locale" usando let:
(let [hex (partial string->integer 16)]
(* (hex "FF") (hex "AA")))
;43350
L'approccio della funzione parziale presenta un vantaggio chiave rispetto agli altri: il consumatore della funzione può ancora decidere quale sarà il valore predefinito anziché il produttore della funzione senza dover modificare la definizione della funzione . Questo è illustrato nell'esempio in hexcui ho deciso che la funzione predefinita decimalnon è quella che voglio.
Un altro vantaggio di questo approccio è che è possibile assegnare alla funzione predefinita un nome diverso (decimale, esadecimale, ecc.) Che può essere più descrittivo e / o un ambito diverso (var, locale). Se lo si desidera, la funzione parziale può anche essere miscelata con alcuni degli approcci precedenti:
(defn string->integer
([s] (string->integer s 10))
([base s] (Integer/parseInt s base)))
(def hex (partial string->integer 16))
(Nota che questo è leggermente diverso dalla risposta di Brian poiché l'ordine dei parametri è stato invertito per i motivi indicati all'inizio di questa risposta)
Puoi anche consultare (fnil) https://clojuredocs.org/clojure.core/fnil
(recur s 10), usandorecurinvece di ripetere il nome della funzionestring->integer. Ciò renderebbe più semplice rinominare la funzione in futuro. Qualcuno sa qualche motivo per non usarerecurin queste situazioni?