Odpowiedzi:
Funkcja może mieć wiele podpisów, jeśli sygnatury różnią się pod względem liczby. Możesz użyć tego do podania wartości domyślnych.
(defn string->integer
([s] (string->integer s 10))
([s base] (Integer/parseInt s base)))
Zauważ, że założenie false
i nil
oba są uważane za nie-wartości, (if (nil? base) 10 base)
można skrócić do (if base base 10)
lub dalej do (or base 10)
.
recur
działa tylko na tej samej mocy. jeśli próbowałeś powtórzyć powyżej, na przykład:java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
(string->integer s 10)
)?
Możesz także zniszczyć rest
argumenty jako mapę od Clojure 1.2 [ ref ]. Pozwala to nazwać i podać wartości domyślne dla argumentów funkcji:
(defn string->integer [s & {:keys [base] :or {base 10}}]
(Integer/parseInt s base))
Teraz możesz zadzwonić
(string->integer "11")
=> 11
lub
(string->integer "11" :base 8)
=> 9
Możesz to zobaczyć w akcji tutaj: https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (na przykład)
To rozwiązanie jest tym bliższe duchowi oryginalnego rozwiązania , ale jest minimalnie czystsze
(defn string->integer [str & [base]]
(Integer/parseInt str (or base 10)))
Podobny wzór , które mogą być przydatne zastosowania or
w połączeniu zlet
(defn string->integer [str & [base]]
(let [base (or base 10)]
(Integer/parseInt str base)))
Chociaż w tym przypadku jest bardziej rozwlekły, może być przydatny, jeśli chcesz, aby wartości domyślne zależały od innych wartości wejściowych . Na przykład rozważmy następującą funkcję:
(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
To podejście można łatwo rozszerzyć tak, aby pracowało z nazwanymi argumentami (jak w rozwiązaniu M. Gilliara):
(defn exemplar [a & {:keys [b c]}]
(let [b (or b 5)
c (or c (* 7 b))]
(println a b c)))
Lub używając jeszcze więcej fuzji:
(defn exemplar [a & {:keys [b c] :or {b 5}}]
(let [c (or c (* 7 b))]
(println a b c)))
or
or
różni się od, :or
ponieważ or
nie zna różnicy nil
i false
.
Jest jeszcze jedno podejście, które warto rozważyć: funkcje częściowe . Są to prawdopodobnie bardziej „funkcjonalne” i bardziej elastyczne sposoby określania wartości domyślnych funkcji.
Zacznij od utworzenia (jeśli to konieczne) funkcji, której parametr (y) chcesz podać jako domyślne jako parametry wiodące:
(defn string->integer [base str]
(Integer/parseInt str base))
Dzieje się tak, ponieważ wersja Clojure partial
umożliwia podanie wartości „domyślnych” tylko w kolejności, w jakiej pojawiają się one w definicji funkcji. Po uporządkowaniu parametrów zgodnie z życzeniem można utworzyć „domyślną” wersję funkcji za pomocą partial
funkcji:
(partial string->integer 10)
Aby ta funkcja była wywoływana wiele razy, możesz umieścić ją w zmiennej za pomocą def
:
(def decimal (partial string->integer 10))
(decimal "10")
;10
Możesz także utworzyć „lokalną wartość domyślną” za pomocą let
:
(let [hex (partial string->integer 16)]
(* (hex "FF") (hex "AA")))
;43350
Podejście do funkcji częściowych ma jedną kluczową przewagę nad innymi: odbiorca funkcji nadal może zdecydować, jaka będzie wartość domyślna, a nie producent funkcji, bez konieczności modyfikowania definicji funkcji . Jest to zilustrowane na przykładzie, w hex
którym zdecydowałem, że domyślna funkcja decimal
nie jest tym, czego chcę.
Kolejną zaletą tego podejścia jest możliwość przypisania funkcji domyślnej innej nazwy (dziesiętnej, szesnastkowej itp.), Która może być bardziej opisowa i / lub mieć inny zakres (zmienna, lokalna). W razie potrzeby funkcję częściową można również łączyć z niektórymi z powyższych podejść:
(defn string->integer
([s] (string->integer s 10))
([base s] (Integer/parseInt s base)))
(def hex (partial string->integer 16))
(Zauważ, że różni się to nieco od odpowiedzi Briana, ponieważ kolejność parametrów została odwrócona z powodów podanych na górze tej odpowiedzi)
Możesz również zajrzeć do (fnil)
https://clojuredocs.org/clojure.core/fnil
(recur s 10)
, używającrecur
zamiast powtarzać nazwę funkcjistring->integer
. Ułatwiłoby to w przyszłości zmianę nazwy funkcji. Czy ktoś zna jakiś powód, aby nie używaćrecur
w takich sytuacjach?