Clojure - іменовані аргументи


Відповіді:


119

У Clojure 1.2 ви можете деструктурувати restаргумент так само, як і карту. Це означає, що ви можете виконувати іменовані позиційні аргументи ключових слів. Ось приклад:

user> (defn blah [& {:keys [key1 key2 key3]}] (str key1 key2 key3))
#'user/blah
user> (blah :key1 "Hai" :key2 " there" :key3 10)
"Hai there10"
user> (blah :key1 "Hai" :key2 " there")
"Hai there"
user> (defn blah [& {:keys [key1 key2 key3] :as everything}] everything)
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
{:key2 " there", :key1 "Hai"}

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

user> (defn blah [& {:keys [key1 key2 key3] :or {key3 10}}] (str key1 key2 key3))
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
"Hai there10"

Але це в Clojure 1.2. Крім того, у старих версіях ви можете зробити це, щоб імітувати те саме:

user> (defn blah [& rest] (let [{:keys [key1 key2 key3] :or {key3 10}} (apply hash-map rest)] (str key1 key2 key3)))
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
"Hai there10"

і це працює, як правило, однаково.

А також ви можете мати позиційні аргументи, які стоять перед аргументами ключового слова:

user> (defn blah [x y & {:keys [key1 key2 key3] :or {key3 10}}] (str x y key1 key2 key3))
#'user/blah
user> (blah "x" "Y" :key1 "Hai" :key2 " there")
"xYHai there10"

Вони не є обов’язковими та повинні бути надані.

Ви можете фактично деструктурувати restаргумент, як будь-яку колекцію Clojure.

user> (defn blah [& [one two & more]] (str one two "and the rest: " more))
#'user/blah
user> (blah 1 2 "ressssssst")
"12and the rest: (\"ressssssst\")"

Ви можете робити подібні речі навіть у Clojure 1.1. Деструктуризація стилю карти для аргументів ключових слів увійшла лише в 1.2.


34

На додаток до чудової відповіді Рейнеса, у clojure-contrib також є макрос, який полегшує життя:

користувач => (використовувати '[clojure.contrib.def: лише [defnk]])
нуль
користувач => (defnk foo [ab: c 8: d 9] 
         [а Б В Г])
# 'користувач / foo
користувач => (foo 1 2)
[1 2 8 9]
користувач => (foo 1 2 3)
java.lang.IllegalArgumentException: Не вказано значення для ключа: 3 (NO_SOURCE_FILE: 0)
користувач => (foo 1 2: c 3)
[1 2 3 9]

7
Я забув це згадати! Мене всі встигли показати 10 тисяч способів, як Клоджуре може деструктурувати речі. : p
Rayne

1
clojure-contrib застарілий, і я не зміг знайти поточну альтернативу. Будь-які ідеї?
Lstor


4

Починаючи з версії Clojure 1.8, підтримка ключових слів все ще здається трохи захопленою .

Ви можете вказати такі аргументи ключових слів:

(defn myfn1
  "Specifying keyword arguments without default values"
  [& {:keys [arg1 arg2]}]
  (list arg1 arg2))

Приклади його виклику:

(myfn1 :arg1 23 :arg2 45)  --> evaluates to (23 45)
(myfn1 :arg1 22)           --> evaluates to (22 nil)

Якщо ви хочете вказати значення за замовчуванням для цих аргументів ключового слова:

(defn myfn2
  "Another version, this time with default values specified"
  [& {:keys [arg1 arg2] :or {arg1 45 arg2 55}}]
  (list arg1 arg2))

Це робить очікувану річ у другому випадку:

(myfn2 :arg1 22)           --> evaluates to (22 55)

У кожній частині кожної мови є плюси і мінуси, але лише для порівняння, ось як ви робите ті самі речі у Common Lisp:

(defun myfn3
    (&key arg1 arg2)
    "Look Ma, keyword args!"
    (list arg1 arg2))

(defun myfn4
    (&key (arg1 45) (arg2 55))
    "Once again, with default values"
    (list arg1 arg2))

0

Ви, мабуть, маєте на увазі іменовані параметри ? Вони не доступні безпосередньо, але ви можете використовувати цей підхід до векторів, якщо хочете, що може дати вам те, що ви хочете.

У RosettaCode є більш глибоке пояснення того, як це зробити за допомогою деструктуризації.


3
@Abel Чи можете ви поділитися прикладами, на які ви посилаєтесь? (У них є спосіб змінити або застаріти).
David J.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.