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

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

4 ответа

Решение

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

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

user => (используйте '[clojure.contrib.def:only [defnk]])
ноль
user=> (defnk foo [a b:c 8:d 9] 
         [ABCD])
#'Пользователь / Foo
пользователь => (foo 1 2)
[1 2 8 9]
пользователь => (foo 1 2 3)
java.lang.IllegalArgumentException: не указано значение для ключа: 3 (NO_SOURCE_FILE:0)
user=> (foo 1 2:c 3)
[1 2 3 9]

Начиная с 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))

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

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

Другие вопросы по тегам