Стандартная версия или идиоматическое использование (fn [f & args] (применить f args))
Каждый раз так часто я нахожу, что хочу применить набор функций к нескольким наборам параметров. Это легко сделать с картой и очень простой функцией.
(map
(fn invoke [f & args] (apply f args))
[- + *]
[1 2 3]
[1 2 3]
[1 2 3])
(-1 6 27)
Поиск в Интернете вызывает довольно много библиотек, которые определяют аналогичную функцию, часто называемую funcall или invoke. Из-за Clojure склонности к вариационным функциям я не могу не думать, что должна быть стандартная версия этой функции.
Есть ли, или есть другой идиоматический способ решения таких ситуаций?
Edit:
Другая форма может быть
(map
(comp eval list)
[- + *]
[1 2 3]
[1 2 3]
[1 2 3])
(-1 6 27)
Что меня пугает из-за eval.
Ответы
Ответ 1
В стандартной библиотеке Clojure нет такой функции funcall
или эквивалентной функции, которая работает именно так. "apply" довольно близок, но в конце нуждается в наборе аргументов, а не в чисто вариационном.
С учетом этого вы можете "обмануть" приложение, чтобы заставить его работать следующим образом, добавив бесконечный список нильсов в конец (которые рассматриваются как пустые последовательности дополнительных аргументов):
(map apply [- + *] [1 2 3] [1 2 3] [1 2 3] (repeat nil))
=> (-1 6 27)
В целом, я думаю, что разумный подход, если вы действительно хотите использовать эту функцию, - это просто определить ее:
(defn funcall [f & ps]
(apply f ps))
(map funcall [- + *] [1 2 3] [1 2 3] [1 2 3])
=> (-1 6 27)
Ответ 2
Если у вас действительно нет подсказки о имени функции, но вы знаете, что должно быть входом и выходом, вы можете попробовать https://github.com/Raynes/findfn.
(find-arg [-1 6 27] map '% [- + *] [1 2 3] [1 2 3] [1 2 3])
;=> (clojure.core/trampoline)
Это говорит нам, что
(map trampoline [- + *] [1 2 3] [1 2 3] [1 2 3])
;=> (-1 6 27)
Собственно, вы можете использовать батут в качестве funcall в clojure. Но это вряд ли идиоматично, потому что это Lisp -1. Вышеприведенный код оценивается как:
[(trampoline - 1 1 1), (trampoline + 2 2 2), (trampoline * 3 3 3)]
, который затем становится
[-1 6 27]
(точнее, в форме a lazyseq).
Как указывает Адриан Муат в комментарии ниже, это, вероятно, не является предпочтительным способом его решения. Использование funcall like construct немного пахнет. Должно быть более чистое решение. Пока вы не найдете этого, findfn
может быть полезным;-).
Ответ 3
Изменить: это будет делать то, что вы хотите (как упоминал @BrandonH):
(map #(apply %1 %&) [- + *] [1 2 3] [1 2 3] [1 2 3])
Но это вряд ли улучшает вашу версию - она просто использует сокращенную версию для анонимных функций.
Я понимаю, что FUNCALL
необходим в Common Lisp, так как он Lisp -2, тогда как Clojure - это Lisp -1.
Ответ 4
(map #(%1 %2 %3 %4) [- + *][1 2 3][1 2 3][1 2 3])
(-1 6 27)
Проблема в том, что если вы хотите разрешить переменное количество аргументов, синтаксис и синтаксис помещает значения в вектор, что требует использования apply. Ваше решение выглядит хорошо для меня, но, как указывает Brandon H, вы можете сократить его до #(apply %1 %&)
.
Как отмечали другие ответчики, это не имеет никакого отношения к funcall
, который, как мне кажется, используется в других Lisps, чтобы избежать двусмысленности между символами и функциями (обратите внимание, что я назвал функцию (%1 ...)
здесь, а не (funcall %1 ...)
.
Ответ 5
Я лично считаю, что ваша первая версия довольно ясная и идиоматичная.
Вот альтернатива, которую вы можете найти интересной, однако:
(map
apply
[- + *]
(map vector [1 2 3] [1 2 3] [1 2 3]))
=> (-1 6 27)
Обратите внимание на трюк использования (map vector....), чтобы транспонировать последовательность аргументов в ([1 1 1] [2 2 2] [3 3 3]), чтобы они могли использоваться в функции приложения.
Ответ 6
Другой подход, который справедливо объясняет себя: "для каждой n-й функции примените его ко всем n-м элементам векторов":
(defn my-juxt [fun-vec & val-vecs]
(for [n (range 0 (count fun-vec))]
(apply (fun-vec n) (map #(nth % n) val-vecs))))
user> (my-juxt [- + *] [1 2 3] [1 2 3] [1 2 3])
(-1 6 27)
Ответ 7
Я не могу сейчас использовать функцию clojure.core, которую вы могли бы подключить к своей карте и заставить ее делать то, что вы хотите. Итак, я бы сказал, просто используйте свою собственную версию.
Мэтт, вероятно, прав, что причина, по которой нет funcall, заключается в том, что вам вряд ли когда-либо понадобится ее в Lisp -1 (смысл, функции и другие привязки имеют одно и то же пространство имен в clojure)
Ответ 8
Как насчет этого? Он выбирает соответствующие возвращаемые значения из juxt. Поскольку это все лениво, он должен вычислять только нужные элементы.
user> (defn my-juxt [fns & colls]
(map-indexed (fn [i e] (e i))
(apply map (apply juxt fns) colls)))
#'user/my-juxt
user> (my-juxt [- + *] [1 2 3] [1 2 3] [1 2 3])
(-1 6 27)