Как создать значение по умолчанию для аргумента функции в Clojure
Я прихожу с этим:
(defn string->integer [str & [base]]
(Integer/parseInt str (if (nil? base) 10 base)))
(string->integer "10")
(string->integer "FF" 16)
Но это должен быть лучший способ сделать это.
Ответы
Ответ 1
Функция может иметь несколько сигнатур, если сигнатуры отличаются по arity. Вы можете использовать это для предоставления значений по умолчанию.
(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)
.
Ответ 2
Вы также можете разрушить аргументы 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 (например)
Ответ 3
Это решение является ближе к духу исходного решения, но незначительно очищается
(defn string->integer [str & [base]]
(Integer/parseInt str (or base 10)))
A аналогичный шаблон, который может быть удобно использовать 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)))
Ответ 4
Существует другой подход, который вы можете рассмотреть: partial. Это, возможно, более "функциональный" и более гибкий способ указания значений по умолчанию для функций.
Начните с создания (при необходимости) функции, которая имеет параметры (ы), которые вы хотите предоставить как по умолчанию (s), как ведущий параметр (ы):
(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))
(Обратите внимание, что это немного отличается от ответа Брайана, поскольку порядок параметров был отменен по причинам, указанным в верхней части этого ответа)
Ответ 5
Вы также можете посмотреть (fnil)
https://clojuredocs.org/clojure.core/fnil