如何在Clojure中确定函数的参数个数?

Zub*_*air 16 clojure

鉴于clojure中的函数x,我如何以编程方式获取参数数量的计数?

例如:

(fn a [b c] ...) has has two arguments
(fn a [] ...) has has zero arguments
Run Code Online (Sandbox Code Playgroud)

jua*_*rro 14

如果您有权访问保存该函数的var,则可以通过以下方式访问其元数据来获取参数计数:

(defn arities [v]
  (->> v meta :arglists (map count)))

(defn a [])
(defn b [_ _])

(map arities [#'a #'b])
;= ((0) (2))
Run Code Online (Sandbox Code Playgroud)

arities将返回包含该函数所有arities的seq.这样做的缺点是,对于可变参数vector([_ _ & _]),它将返回(4).

(defn c [_ _ & _])
(arities #'c)
;= (4)
Run Code Online (Sandbox Code Playgroud)

这可以通过&从所有参数列表中删除符号来解决.

(defn arities [v]
  (->> v 
    meta 
    :arglists 
    (map #(remove #{'&} %))
    (map count)))

(arities #'c)
;= (3)
Run Code Online (Sandbox Code Playgroud)

如果您无权访问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)
Run Code Online (Sandbox Code Playgroud)

编辑

在给出另一个回答之后,我意识到定义为变量函数的结果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))
Run Code Online (Sandbox Code Playgroud)

这个反射版本稍微复杂一些,它依赖于更多的实现细节(即"这个或那个函数声明了吗?"或"函数是否扩展了类X?"),所以我不建议使用这种方法.

  • 值得注意的是:argslist可以手动覆盖:`(defn baz {:arglists'([boom])} [ab])`将导致`(:arglists(meta#'baz)); =>(繁荣])` (3认同)