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)))

これは、90 個の入力ノードを持つニューラル ネットワークを呼び出す DSL ステートメントです。もちろん回避できますが、制限がどこから来るのか疑問に思っていました。ありがとう。

4

2 に答える 2

17

まず第一に、制限は必要な位置引数にのみ適用されます。必要な数の引数を処理するために、変数アリティ ケース (& 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個の位置引数とその上に引数を指定すると、明らかに奇妙なことが起こることに注意してください。

理論的根拠については、これは、JVM がアリティによって非常に効率的にメソッドにディスパッチできるという事実に関係していると思います。そのため、clojure.lang.Fnクラスにはinvoke最大 20 のアリティに対してオーバーロードされたメソッドがあります。うまくいくかどうかは完全にはわかりませんそれよりも高いですが、これは人々が頻繁に必要とするものではないと思います...つまり、関数に20を超える位置引数を指定するAPIは確かに少し疑わしいと思います。

于 2010-04-29T10:44:44.777 に答える
11

Michal Marczyk の回答は、制限を回避する方法を非常によく示しています。この制限の理由に興味がある場合は、次の clojure ソースを一瞥することをお勧めします: IFn

Invoke は、Ifn インターフェイスによって実装された Java オーバーロード メソッドであり、Rich はそれをオーバーロードして、最大 20 個の引数をサポートしました。clojure で関数を呼び出すと、基礎となる実装は、関数オブジェクトで呼び出します。これは、最大 20 個の引数のみをサポートするメソッドです。

オーバーロードしてサポートを増やすこともできますが、その有用性については疑問です。入力ソースがたくさんある場合は、おそらく配列として扱うことができます。

于 2010-04-29T14:47:55.930 に答える