Как создать значение по умолчанию для аргумента функции в 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))

(Обратите внимание, что это немного отличается от ответа Брайана, поскольку порядок параметров был отменен по причинам, указанным в верхней части этого ответа)