16

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

例如:

(fn a [b c] ...) has has two arguments
(fn a [] ...) has has zero arguments
4

2 回答 2

15

如果您有权访问包含该函数的 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 类?”),所以我不建议使用这种方法.

于 2013-06-19T18:27:20.803 回答
-1

您可以构造一个接受所有参数的函数,将它们放在一个列表中并返回计数,如下所示:

(defn arg-count [& rest] (count rest))
(arg-count) ;; 0
(arg-count 1 2 3) ;; 3
于 2013-06-19T20:16:27.013 回答