Вопрос:

Применение карты к аргументу rest функции

clojure

1687 просмотра

4 ответа

341 Репутация автора

В Clojure, если у меня есть функция f ,

(defn f [& r] ... )

и у меня есть seq args с аргументами, которые я хочу вызвать f, я могу легко использовать apply :

(apply f args)

Теперь, скажем, у меня есть другая функция g , которая предназначена для принятия любого из ряда необязательных именованных аргументов, то есть, где аргумент rest деструктурируется как карта:

(defn g [& {:keys [a b] :as m}] ... )

Я обычно звоню г, делая что-то вроде

(g :a 1 :b 2)

но если у меня есть карта my-map со значением {: a 1: b 2}, и я хочу «применить» g к my-map - другими словами, получить что-то, что в конечном итоге вызовет приведенный выше вызов тогда я, естественно, не мог использовать apply, так как это было бы эквивалентно

(g [:a 1] [:b 2])

Есть хороший способ справиться с этим? Могу ли я сойти с пути в своем дизайне, чтобы закончить с этим? Лучшее решение, которое я могу найти, было бы

(apply g (flatten (seq my-map)))

но мне точно это не нравится Есть лучшие решения?

РЕДАКТИРОВАТЬ: небольшое улучшение предложенного решения может быть

(apply g (mapcat seq my-map))

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

Автор: Marxama Источник Размещён: 02.05.2013 08:39

Ответы (4)


6 плюса

21981 Репутация автора

Я сам наткнулся на эту проблему и в итоге определил функции, ожидающие одну карту. Карта может иметь различное количество пар ключ / значение, и, если она достаточно гибкая, нет необходимости в аргументах & rest. Также нет боли при применении. Делает жизнь намного проще!

(defn g [{:keys [a b] :as m}] ... )
Автор: Michiel Borkent Размещён: 02.05.2013 08:46

3 плюса

8524 Репутация автора

Нет лучшего прямого пути, чем преобразование в последовательность.

Вы сделали. Вы сделали все, что могли.

Просто не очень удобно иметь стиль Common Lisp: ключевые слова arg функции. Если вы посмотрите на код Clojure, то обнаружите, что почти все функции написаны таким образом.

Даже великие RMS не являются их поклонниками

«Одна вещь, которая мне не очень нравится, это аргументы с ключевыми словами (8). Мне они не совсем нравятся. Я сделаю это иногда, но я минимизирую время, когда я это делаю». ( Источник )

В тот момент, когда вам нужно разбить полную хеш-карту на части, чтобы передать их в качестве аргументов, сопоставленных с ключевыми словами, вы должны подвергнуть сомнению свой дизайн функции.

Я обнаружил, что в случае, когда вы хотите передать общие параметры, как :consider-nil trueвы, вероятно, никогда не будете вызывать функцию с хэш-картой {:consider-nil true}.

В случае, если вы хотите выполнить оценку на основе некоторых ключей хэш-карты, вы в 99% случаев имеете f ([m & args])объявление.

Когда я начал определять функции в Clojure, я столкнулся с той же проблемой. Однако, подумав больше о проблемах, которые я пытался решить, я заметил, что практически никогда не использую деструкторы в объявлении функций.

Автор: Leon Grapenthin Размещён: 04.05.2013 05:43

1 плюс

341 Репутация автора

Вот очень упрощенная функция, которую можно использовать в точности так, как она применяется, за исключением того, что последний аргумент (который должен быть картой) будет расширен до: key1 val1: key2 val2 и т. Д.

(defn mapply
  [f & args]
  (apply f (reduce concat (butlast args) (last args))))

Я уверен, что есть более эффективные способы сделать это, и то, хотите ли вы в конечном итоге оказаться в ситуации, когда вам придется использовать такую ​​функцию, обсуждается, но она действительно отвечает на первоначальный вопрос. В основном, я по-детски доволен именем ...

Автор: Marxama Размещён: 08.05.2013 08:54

0 плюса

257 Репутация автора

Самое хорошее решение, которое я нашел:

(apply g (apply concat my-map))
Автор: Ernesto Размещён: 15.10.2017 02:51
Вопросы из категории :
32x32