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 есть более глубокое объяснение того, как сделать это, используя деструктуризацию.