Применение карты к аргументу останова функции
В Clojure, если у меня есть функция f,
(defn f [& r] ... )
и у меня есть seq args с аргументами, которые я хочу вызвать f, я могу легко использовать apply:
(apply f args)
Теперь, скажем, у меня есть еще одна функция g, которая предназначена для принятия любого из нескольких необязательных именованных аргументов - то есть, где аргумент rest разрушен как карта:
(defn g [& {:keys [a b] :as m}] ... )
Обычно я называю g, делая что-то вроде
(g :a 1 :b 2)
но если у меня есть карта my-map со значением {: a 1: b 2}, и я хочу "применить" g к моей карте - другими словами, получить что-то, что закончилось бы как вышеупомянутый вызов, тогда я, естественно, не смог бы использовать apply, так как он был бы эквивалентен
(g [:a 1] [:b 2])
Есть ли хороший способ справиться с этим? Могу ли я уйти с пути в мой дизайн, чтобы закончить с этим? Лучшее решение, которое я могу найти, будет
(apply g (flatten (seq my-map)))
но мне это точно не нравится. Любые лучшие решения?
EDIT: Небольшое улучшение предлагаемого решения может быть
(apply g (mapcat seq my-map))
который, по крайней мере, удаляет один вызов функции, но может все еще не совсем ясно, что происходит.
Ответы
Ответ 1
Я сам наткнулся на эту проблему и в конечном итоге определил функции, чтобы ожидать одну карту. Карта может иметь переменное количество пар ключ/значение и, если она достаточно гибкая, поэтому нет необходимости и аргументов для останова. Также нет боли при применении. Делает жизнь намного проще!
(defn g [{:keys [a b] :as m}] ... )
Ответ 2
Нет лучшего прямого способа, чем преобразование в seq.
Вы закончили. Вы сделали все, что могли.
На самом деле это не так уж сложно, если у вас есть общие функции Lisp style: keyword arg. Если вы посмотрите вокруг кода Clojure, вы обнаружите, что почти ничего не написано.
Даже большая RMS не является их поклонником:
"Одна вещь, которую мне не очень нравится, - это аргументы ключевого слова (8). Они мне не очень нравятся, я иногда это делаю, но я минимизирую время, когда я это делаю". (Источник)
В тот момент, когда вам нужно разбить полную хэш-карту на части, чтобы передать все из них в качестве сопоставленных ключевых слов, вы должны задать вопрос о своей функции.
Я нахожу, что в случае, когда вы хотите передать общие параметры, такие как :consider-nil true
, вы, вероятно, никогда не будете вызывать функцию с хэш-картой {:consider-nil true}
.
В случае, если вы хотите сделать оценку на основе некоторых ключей хэш-карты, вы в 99% случаев имеете объявление f ([m & args])
.
Когда я начал определять функции в Clojure, я попал в ту же проблему. Однако, подумав больше о проблемах, которые я пытался решить, я заметил, что я почти никогда не использовал деструктурирование в объявлении функции.
Ответ 3
Вот очень упрощенная функция, которая может использоваться точно так же, как применимо, за исключением того, что конечный arg (который должен быть картой) будет расширен до: key1 val1: key2 val2 и т.д.
(defn mapply
[f & args]
(apply f (reduce concat (butlast args) (last args))))
Я уверен, что есть более эффективные способы сделать это, и не хотите ли вы оказаться в ситуации, когда вам придется использовать такую функцию, для обсуждения, но она отвечает на оригинал вопрос. В основном, я по-детски доволен именем...
Ответ 4
Самое лучшее решение, которое я нашел:
(apply g (apply concat my-map))