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

Стандартная версия или идиоматическое использование (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.

4b9b3361

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