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 false
e nil
sono entrambi considerati non valori, (if (nil? base) 10 base)
potrebbe essere abbreviato (if base base 10)
o ulteriormente (or base 10)
.
recur
funzioni 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 rest
argomenti 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 or
combinato 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 :or
poiché or
non conosce la differenza di nil
e 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 partial
consente 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 partial
funzione:
(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 hex
cui ho deciso che la funzione predefinita decimal
non è 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)
, usandorecur
invece di ripetere il nome della funzionestring->integer
. Ciò renderebbe più semplice rinominare la funzione in futuro. Qualcuno sa qualche motivo per non usarerecur
in queste situazioni?