Почему существует ограничение до 20 параметров для функции замыкания - PullRequest
11 голосов
/ 29 апреля 2010

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

При определении функции с более чем 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 входными узлами. Можно обойти это, конечно, но было интересно, откуда исходит ограничение. Спасибо.

Ответы [ 2 ]

17 голосов
/ 29 апреля 2010

Прежде всего, ограничение распространяется только на требуемые позиционные аргументы ; вы всегда можете использовать переменную arity case (& 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 позиционных аргументов и & аргумент поверх этого, очевидно, возникнет странность.

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

11 голосов
/ 29 апреля 2010

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

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

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

...