11

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 个输入节点的神经网络。当然可以解决它,但想知道限制来自哪里。谢谢。

4

2 回答 2

17

首先,该限制仅适用于所需的位置参数;您始终可以使用变量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在所述序列等上 - 与为每个项目分配单独的名称相比,使用大量相似项目往往更有意义。

(请注意,我不会假装知道您要转录到 Clojure 中的特定 DSL 的性质或您正在处理的问题类型,只是建议您可能感兴趣的点。如果您有这似乎并不适用的非常时髦的情况,也许您可​​以提供更多详细信息,我们会看看这里是否有人可以提供一些有用的提示来在 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 个位置 args 和&最上面的 arg,显然会出现奇怪的情况。

至于基本原理,我认为这与 JVM 可以非常有效地通过 arity 分派到方法有关,因此clojure.lang.Fn该类的invoke方法重载了最多 20 个 arity。我不完全确定它是否可以去比这更高,但我想这不是人们经常需要的东西......我的意思是,我当然发现任何为函数指定超过 20 个位置参数的 API 有点可疑。

于 2010-04-29T10:44:44.777 回答
11

Michal Marczyk 的回答很好地告诉您如何绕过限制。如果你对这个限制的原因感兴趣,你可能想看看这个 clojure 源代码:IFn

invoke 是由 Ifn 接口实现的 java 重载方法,Rich 重载它以支持最多 20 个参数。在clojure中调用函数时,底层实现调用函数对象的invoke,这个方法最多只支持20个args。

你可以重载它来支持更多,但我怀疑这样的东西的效用。如果您有一堆输入源,它们可能无论如何都可以被视为一个数组。

于 2010-04-29T14:47:55.930 回答