Как объединить явные аргументы с переменными аргументами при вызове функции

#clojure

#clojure

Вопрос:

В JavaScript можно выполнить следующее:

 function foo(arg1, arg2, arg3) {
  ...
}

var others = [ 'two', 'three' ];
foo('one', ...others);  // same as foo('one', 'two', 'three')
  

В Clojure «переменные аргументы» могут быть приняты следующим образом:

 (defn foo [arg1 amp; others]
  ...)
  

Но чтобы передать их в сочетании с другими аргументами, вы должны сделать это:

 (apply foo (concat '("one") others))
  

Что, честно говоря, действительно уродливо. Это также невозможно, когда вам нужно повторить:

 (apply recur (concat '("one") others)) ;; doesn't work
  

Есть ли лучший способ сделать это? И если нет, есть ли вообще какой-либо способ выполнить это в recur случае?

Ответ №1:

Но чтобы передать их в сочетании с другими аргументами, вы должны сделать это: (apply foo (concat '("one") others))

Вам не нужно этого делать: apply также является переменной функцией, которая может принимать аргументы перед конечным аргументом последовательности, например

 (apply foo "one" others)
  

Вы можете передать любое количество отдельных аргументов перед конечным аргументом последовательности в apply .

 user=> (defn foo [arg1 amp; args] (apply println arg1 args))
#'user/foo
user=> (apply foo "one" 2 "three" [4 "five" 6.0])
one 2 three 4 five 6.0
  

Для дальнейшей демонстрации эти вызовы функционально эквивалентны:

 (apply   1 2 [3])
(apply   1 [2 3])
(apply   [1 2 3])
  

Это также невозможно, когда вам нужно повторить

recur это специальная форма, и apply она не работает с ней так, как с обычной функцией Clojure.

есть ли вообще какой-либо способ выполнить это в recur случае?

Не с apply помощью . Вы можете recur использовать переменные аргументы, но вы не можете (apply recur ...) .

Комментарии:

1. «Вам не нужно этого делать: apply также является переменной функцией, которая может принимать аргументы перед последним аргументом последовательности, например (apply foo "one" others) , вы можете передать любое количество отдельных аргументов перед последним аргументом последовательности для применения». Но разве others в этом случае не было бы передано как список, и его члены не были бы «сплющены» в оставшиеся слоты аргументов?

2.@brundolf в этом случае others его члены были бы сглажены / распределены по оставшимся позициям аргументов. (apply 1 2 [3 4]) эквивалентно (apply 1 [2 3 4]) и (apply [1 2 3 4]) .

3. Странно. Какой там точный алгоритм? Конечно, это не может быть «сглаживать каждую последовательность, переданную для применения»? Возможно, «сгладить последний аргумент, если это последовательность»?

4. @brundolf В основном предполагается, что последний параметр представляет собой список. Любые другие аргументы перед ним и после аргумента функции рассматриваются как элементы для cons добавления в список аргументов.

5. Это вполне возможно с recur помощью . Учитывая (defn f [x amp; xs] ...) , есть две переменные, которые необходимо передать любому recur вызову: x и xs . Вам не нужно apply , потому что компилятор точно знает, на какую арность отправляется (текущая). Таким образом: (defn f [x amp; xs] (if foo 0, (recur (inc x) (rest xs)))(