Antworten:
Eine Funktion kann mehrere Signaturen haben, wenn sich die Signaturen in ihrer Arität unterscheiden. Damit können Sie Standardwerte angeben.
(defn string->integer
([s] (string->integer s 10))
([s base] (Integer/parseInt s base)))
Beachten Sie, dass unter der Annahme , false
und nil
beide nicht-Werte betrachtet, (if (nil? base) 10 base)
könnte verkürzt werden (if base base 10)
, oder weiter zu (or base 10)
.
recur
nur mit derselben Arität gearbeitet. Wenn Sie oben versucht haben, zum Beispiel:java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
(string->integer s 10)
)?
Sie können rest
Argumente seit Clojure 1.2 [ ref ] auch als Map zerstören . Auf diese Weise können Sie Funktionsargumente benennen und Standardeinstellungen angeben:
(defn string->integer [s & {:keys [base] :or {base 10}}]
(Integer/parseInt s base))
Jetzt können Sie anrufen
(string->integer "11")
=> 11
oder
(string->integer "11" :base 8)
=> 9
Sie können dies hier in Aktion sehen: https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (zum Beispiel)
Diese Lösung ist dem Geist der ursprünglichen Lösung näher , aber geringfügig sauberer
(defn string->integer [str & [base]]
(Integer/parseInt str (or base 10)))
Ein ähnliches Muster, das nützlich sein kann, or
kombiniert mitlet
(defn string->integer [str & [base]]
(let [base (or base 10)]
(Integer/parseInt str base)))
In diesem Fall ist dies zwar ausführlicher, es kann jedoch hilfreich sein, wenn die Standardeinstellungen von anderen Eingabewerten abhängen sollen . Betrachten Sie beispielsweise die folgende Funktion:
(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
Dieser Ansatz kann leicht erweitert werden, um auch mit benannten Argumenten zu arbeiten (wie in der Lösung von M. Gilliar):
(defn exemplar [a & {:keys [b c]}]
(let [b (or b 5)
c (or c (* 7 b))]
(println a b c)))
Oder noch mehr Fusion:
(defn exemplar [a & {:keys [b c] :or {b 5}}]
(let [c (or c (* 7 b))]
(println a b c)))
or
or
ist anders als :or
da or
kennt den Unterschied von nil
und nicht false
.
Es gibt noch einen anderen Ansatz, den Sie in Betracht ziehen sollten: Teilfunktionen . Dies ist wohl eine "funktionalere" und flexiblere Möglichkeit, Standardwerte für Funktionen anzugeben.
Erstellen Sie zunächst (falls erforderlich) eine Funktion mit den Parametern, die Sie als Standardparameter als Hauptparameter bereitstellen möchten:
(defn string->integer [base str]
(Integer/parseInt str base))
Dies geschieht, weil Sie mit Clojures Version von partial
die "Standard" -Werte nur in der Reihenfolge angeben können, in der sie in der Funktionsdefinition angezeigt werden. Nachdem die Parameter wie gewünscht bestellt wurden, können Sie mit der folgenden partial
Funktion eine "Standard" -Version der Funktion erstellen :
(partial string->integer 10)
Um diese Funktion mehrmals aufrufbar zu machen, können Sie sie in eine Variable einfügen, indem Sie Folgendes verwenden def
:
(def decimal (partial string->integer 10))
(decimal "10")
;10
Sie können auch einen "lokalen Standard" erstellen, indem Sie let
:
(let [hex (partial string->integer 16)]
(* (hex "FF") (hex "AA")))
;43350
Der Teilfunktionsansatz hat einen entscheidenden Vorteil gegenüber den anderen: Der Benutzer der Funktion kann weiterhin entscheiden, wie hoch der Standardwert sein soll, und nicht der Hersteller der Funktion, ohne die Funktionsdefinition ändern zu müssen . Dies wird in dem Beispiel veranschaulicht, in dem hex
ich entschieden habe, dass die Standardfunktion decimal
nicht das ist, was ich will.
Ein weiterer Vorteil dieses Ansatzes besteht darin, dass Sie der Standardfunktion einen anderen Namen (dezimal, hexadezimal usw.) zuweisen können, der möglicherweise aussagekräftiger ist und / oder einen anderen Bereich (var, local). Falls gewünscht, kann die Teilfunktion auch mit einigen der oben genannten Ansätze gemischt werden:
(defn string->integer
([s] (string->integer s 10))
([base s] (Integer/parseInt s base)))
(def hex (partial string->integer 16))
(Beachten Sie, dass dies geringfügig von Brians Antwort abweicht, da die Reihenfolge der Parameter aus den oben in dieser Antwort angegebenen Gründen umgekehrt wurde.)
Sie können sich auch https://clojuredocs.org/clojure.core/fnil ansehen(fnil)
(recur s 10)
,recur
anstatt den Funktionsnamen zu wiederholenstring->integer
. Das würde es in Zukunft einfacher machen, die Funktion umzubenennen. Kennt jemand einen Grund,recur
in diesen Situationen nicht zu verwenden ?