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 falsei niloba 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).
recurdział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ć restargumenty 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 orw 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
orróżni się od, :orponieważ ornie zna różnicy nili 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 partialumoż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ą partialfunkcji:
(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 hexktórym zdecydowałem, że domyślna funkcja decimalnie 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ącrecurzamiast 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ćrecurw takich sytuacjach?