clojure で関数 x が与えられた場合、引数の数をプログラムで取得するにはどうすればよいですか?
例えば:
(fn a [b c] ...) has has two arguments
(fn a [] ...) has has zero arguments
clojure で関数 x が与えられた場合、引数の数をプログラムで取得するにはどうすればよいですか?
例えば:
(fn a [b c] ...) has has two arguments
(fn a [] ...) has has zero arguments
関数を保持する var にアクセスできる場合は、次の方法でメタデータにアクセスすることで引数の数を取得できます。
(defn arities [v]
(->> v meta :arglists (map count)))
(defn a [])
(defn b [_ _])
(map arities [#'a #'b])
;= ((0) (2))
arities
関数のすべてのアリティを含む seq を返します。これには、可変引数ベクトル ( [_ _ & _]
) に対して (4) が返されるという欠点があります。
(defn c [_ _ & _])
(arities #'c)
;= (4)
&
これは、すべての引数リストからシンボルを削除することで修正できます。
(defn arities [v]
(->> v
meta
:arglists
(map #(remove #{'&} %))
(map count)))
(arities #'c)
;= (3)
var にアクセスできない場合は、関数の引数カウントを検出するために使用した小さな関数を次に示します。リフレクションを使用するため、優れたパフォーマンスが必要な場合に使用するアプローチではありません。また、実装の詳細に依存することも考慮してください。
(defn n-args [f]
(-> f class .getDeclaredMethods first .getParameterTypes alength))
(defn a [])
(defn b [_ _])
(defn c [_ _ & _])
(map n-args [a b c])
;= (0 2 3)
編集
答えをもう一度読んだ後、 として定義された可変引数関数の結果 3 は、(defn x [_ _ & _] ,,,)
3 つの引数を持つ関数に対して得られる結果と同じであるため、実際にはかなり誤解を招くことに気付きました。次のバージョン:variadic
では、シンボルを含む引数ベクトルに対して、特定の数値の代わりにが返されます (実際の引数名の場合&
を除く)。Jeremy Heilerのコメントで述べたように、メタデータから引数カウントを取得することは、値が手動で変更されていない場合にのみ機能します。[&]
&
:arglists
(defn a [_])
(defn b [_ _])
(defn c [_ _ & _])
(defn d [_ _ _])
(defn e [&])
(defn variadic? [s]
(and (some #{'&} s)
(not (every? #{'&} s))))
(defn arities [v]
(->> v
meta
:arglists
(map #(if (variadic? %) :variadic %))
(map #(if (sequential? %) (count %) %))))
(map arities [#'a #'b #'c #'d #'e])
;= ((1) (2) (:variadic) (3) (:variadic))
これに対するリフレクション バージョンはもう少し複雑で、より多くの実装の詳細 (つまり、「これまたはその関数は宣言されていますか?」または「関数はクラス X を拡張しますか?」) に依存しているため、そのアプローチを使用することはお勧めしません。 .
すべての引数を取り、それらをリストに入れ、次のようにカウントを返す関数を作成できます。
(defn arg-count [& rest] (count rest))
(arg-count) ;; 0
(arg-count 1 2 3) ;; 3