Почему существует предел максимальных 20 параметров функции clojure

как представляется, существует ограничение на количество параметров, которые может выполнять функция a clojure.

При определении функции с более чем 20 параметрами я получаю следующее:

#<CompilerException java.lang.RuntimeException: java.lang.RuntimeException: java.lang.Exception: Can't specify more than 20 params (NO_SOURCE_FILE:0) (NO_SOURCE_FILE:0)>

Очевидно, этого можно избежать, но я ударил этот предел, портируя модель исполнения существующего DSL на clojure, и у меня есть конструкции в моей DSL, как показано ниже, которые путем макрорасширения могут быть легко сопоставлены с функциями кроме этого предела:

(defAlias nn1 ((element ?e1) (element ?e2)) number
"@doc features of the elements are calculated for entry into
      the first neural network, the result is the score computed by the latter"
(nn1-recall (nn1-feature00 ?e1 ?e2) (nn1-feature01 ?e1 ?e2) ... (nn1-feature89 ?e1 ?e2)))

который является оператором DSL для вызова нейронной сети с 90 входными узлами. Конечно, можно обойти это, но было интересно, откуда взялся предел. Спасибо.

Ответ 1

Прежде всего, предел применяется только к требуемым позиционным аргументам; вы всегда можете использовать случай переменной arity (& more-args в сигнатуре функции), чтобы обрабатывать столько аргументов, сколько хотите:

(defn foo [& args]
  (count args))

(foo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25)
;; returns 25

На самом деле, на первый взгляд, & args скорее всего будет правильным решением вашей проблемы. (Например, вы сможете сопоставить функцию над вашими входными узлами, собранными в последовательность, loop/recur по указанной последовательности и т.д. - имеет тенденцию иметь большее значение с большим количеством похожих элементов, чем назначать отдельные имена к каждому из них.

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

Для полноты вы можете добавить бит & args к функции, которая принимает первые 19 аргументов в качестве необходимых позиционных аргументов:

(defn bar [a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 & as]
  (+ 19 (count as)))

(bar 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25)
;; returns 25

Обратите внимание, что если вы укажете 20 позиционных аргументов и & arg, то, возможно, произойдет странность.

Что касается обоснования, я полагаю, что это связано с тем, что JVM может очень эффективно отправлять метод по arity, поэтому класс clojure.lang.Fn имеет метод invoke, перегруженный для явлений до 20. я "Не совсем уверен, может ли он подняться выше, но я полагаю, что это не то, что люди требуют этого часто... Я имею в виду, что я, конечно, нахожу API, определяющий более 20 позиционных аргументов функции, немного подозрительной.

Ответ 2

Ответ Michal Marczyk говорит вам очень хорошо, как обойти предел. Если вас интересует причина этого ограничения, вы можете взглянуть на этот бит источника clojure: IFn

вызывается как перегруженный Java-метод, реализованный интерфейсом Ifn, Rich перегружает его, чтобы поддерживать до 20 аргументов. Когда вы вызываете функцию в clojure, вызовы базовой реализации вызывают на объект функции, метод, который поддерживает только до 20 аргументов.

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