Подтвердить что ты не робот

Применение карты к аргументу останова функции

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

который, по крайней мере, удаляет один вызов функции, но может все еще не совсем ясно, что происходит.

4b9b3361

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