Ответы:
Функция может иметь несколько подписей, если подписи различаются по арности. Вы можете использовать это для предоставления значений по умолчанию.
(defn string->integer
([s] (string->integer s 10))
([s base] (Integer/parseInt s base)))
Обратите внимание, что оба предположения false
и nil
считаются не значениями, (if (nil? base) 10 base)
их можно сократить до (if base base 10)
или далее до (or base 10)
.
recur
работает только с той же арностью. если вы пробовали повторять выше, например:java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
(string->integer s 10)
)?
Вы также можете деструктурировать rest
аргументы как карту, начиная с Clojure 1.2 [ ref ]. Это позволяет вам назвать и указать значения по умолчанию для аргументов функции:
(defn string->integer [s & {:keys [base] :or {base 10}}]
(Integer/parseInt s base))
Теперь ты можешь позвонить
(string->integer "11")
=> 11
или
(string->integer "11" :base 8)
=> 9
Вы можете увидеть это в действии здесь: https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (например)
Это решение ближе к духу оригинального решения , но немного чище.
(defn string->integer [str & [base]]
(Integer/parseInt str (or base 10)))
Аналогичная картина , которая может быть удобно использование в or
сочетании сlet
(defn string->integer [str & [base]]
(let [base (or base 10)]
(Integer/parseInt str base)))
Хотя в этом случае более подробный, он может быть полезен, если вы хотите, чтобы значения по умолчанию зависели от других входных значений . Например, рассмотрим следующую функцию:
(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
Этот подход можно легко расширить для работы с именованными аргументами (как в решении М. Гиллиара):
(defn exemplar [a & {:keys [b c]}]
(let [b (or b 5)
c (or c (* 7 b))]
(println a b c)))
Или используя еще больше фьюжн:
(defn exemplar [a & {:keys [b c] :or {b 5}}]
(let [c (or c (* 7 b))]
(println a b c)))
or
or
отличается от :or
так or
как не знает разницы между nil
и false
.
Вы можете рассмотреть еще один подход: частичные функции. Возможно, это более «функциональный» и более гибкий способ указания значений по умолчанию для функций.
Начните с создания (при необходимости) функции, которая имеет параметр (ы), который вы хотите предоставить по умолчанию в качестве ведущего параметра (ов):
(defn string->integer [base str]
(Integer/parseInt str base))
Это сделано потому, что версия Clojure partial
позволяет вам указывать значения «по умолчанию» только в том порядке, в котором они появляются в определении функции. После того, как параметры будут упорядочены по желанию, вы можете создать версию функции по умолчанию, используя partial
функцию:
(partial string->integer 10)
Чтобы сделать эту функцию вызываемой несколько раз, вы можете поместить ее в var, используя def
:
(def decimal (partial string->integer 10))
(decimal "10")
;10
Вы также можете создать «локальное значение по умолчанию», используя let
:
(let [hex (partial string->integer 16)]
(* (hex "FF") (hex "AA")))
;43350
Подход с частичной функцией имеет одно ключевое преимущество перед другими: потребитель функции все еще может решить, какое будет значение по умолчанию, а не производитель функции, без необходимости изменять определение функции . Это проиллюстрировано в примере, в hex
котором я решил, что функция по умолчанию decimal
- это не то, что мне нужно.
Еще одно преимущество этого подхода заключается в том, что вы можете присвоить функции по умолчанию другое имя (десятичное, шестнадцатеричное и т. Д.), Которое может быть более наглядным и / или иметь другую область действия (var, local). При желании частичную функцию также можно смешать с некоторыми из вышеперечисленных подходов:
(defn string->integer
([s] (string->integer s 10))
([base s] (Integer/parseInt s base)))
(def hex (partial string->integer 16))
(Обратите внимание, что это немного отличается от ответа Брайана, поскольку порядок параметров был изменен на обратный по причинам, указанным в верхней части этого ответа)
Вы также можете посмотреть (fnil)
https://clojuredocs.org/clojure.core/fnil
(recur s 10)
, используяrecur
вместо повторения имя функцииstring->integer
. Это упростило бы переименование функции в будущем. Кто-нибудь знает причину не использоватьrecur
в таких ситуациях?