Clojure - названные аргументы

Имеет ли Clojure именованные аргументы? Если да, можете ли вы предоставить небольшой пример?

Ответ 1

В 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.

Ответ 2

В дополнение к отличному ответу Рейнса, макрос в clojure -contrib, который облегчает жизнь:

user=> (use '[clojure.contrib.def :only [defnk]])
nil
user=> (defnk foo [a b :c 8 :d 9] 
         [a b c d])
#'user/foo
user=> (foo 1 2)
[1 2 8 9]
user=> (foo 1 2 3)
java.lang.IllegalArgumentException: No value supplied for key: 3 (NO_SOURCE_FILE:0)
user=> (foo 1 2 :c 3)
[1 2 3 9]

Ответ 3

Возможно, вы имеете в виду именованные параметры? Они недоступны, но вы можете использовать этот подход для векторов, если хотите, что может дать вам то, что вы хотите.

В RosettaCode есть более подробное объяснение того, как это сделать, используя деструктурирование.

Ответ 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))