Як створити значення за замовчуванням для аргументу функції в Clojure


127

Я приходжу з цим:

(defn string-> integer [str & [base]]
  (Integer / parseInt str (якщо (нуль? База) 10 баз)))

(рядок-> ціле число "10")
(рядок-> ціле число "FF" 16)

Але це має бути кращим способом зробити це.

Відповіді:


170

Функція може мати декілька підписів, якщо підписи відрізняються за сутністю. Ви можете використовувати це для введення значень за замовчуванням.

(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).


3
Я думаю, що було б краще сказати другий рядок (recur s 10), використовуючи recurзамість повторення назви функції string->integer. Це полегшить перейменування функції в майбутньому. Хтось знає якусь причину не використовувати recurв цих ситуаціях?
Rory O'Kane

10
Схоже, recurпрацює лише на одній аристократиці. якщо ви спробували повторити вище, наприклад:java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
djhaskin987

Побіг у цьому ж питанні. Чи має сенс мати сам виклик функції (тобто (string->integer s 10))?
Курт Мюллер

155

Ви також можете деструктурувати 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 (наприклад)


1
Так легко зрозуміти, якщо йде з Python фоном :)
Dan

1
Це набагато простіше зрозуміти, ніж прийняту відповідь ... це прийнятий "клоурійський" спосіб? Будь ласка, розглянути питання про додавання до цього документа
Droogans

1
Я додав питання до неофіційного посібника зі стилю, щоб допомогти вирішити це.
Droogans

1
Ця відповідь ефективніше фіксує «правильний» спосіб зробити це, ніж прийнята відповідь, хоча і те, і інше буде добре працювати. (звичайно, велика сила мов Lisp полягає в тому, що зазвичай існує багато різних способів зробити одну і ту ж фундаментальну справу)
johnbakers

2
Це виглядало для мене трохи словно, і я певний час запам’ятав це, тому я створив трохи менш багатослівний макрос .
akbiggs

33

Це рішення ближче до духу оригінального рішення , але незначно чистіше

(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
johnbakers

Я Cloobre noob, тому, можливо, OpenLearner має рацію, але це цікава альтернатива рішення Метью вище. Я радий про це знати, чи вирішу я в кінцевому рахунку використовувати його чи ні.
GlenPeterson

orвідрізняється від :orтого, orщо не знає різниці nilі false.
Сянгру Ліан

@XiangruLian Ви хочете сказати, що при використанні: або, якщо ви передасте помилкове значення, воно буде знати помилкове значення замість типового? Хоча з або він буде використовувати за замовчуванням, коли передано false, а не false?
Дідьє А.

8

Ви можете скористатися ще одним підходом: часткові функції. Це, мабуть, більш "функціональний" і гнучкіший спосіб визначення значень за замовчуванням для функцій.

Почніть зі створення (за необхідності) функції, яка має параметри (параметри), які ви хочете надати за замовчуванням, як провідні параметри:

(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))

(Зауважте, це дещо відрізняється від відповіді Брайана, оскільки порядок параметрів змінено з причин, наведених у верхній частині цієї відповіді)


1
Це не те, про що задається питання; це цікаво, хоча.
Заз

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.