Clojure:如何在运行时找出函数的arity?

时间:2009-11-08 14:17:03

标签: functional-programming clojure

给定一个函数对象或名称,我该如何确定它的arity?类似于(arity func-name)

我希望有一种方法,因为arity在Clojure中非常重要

7 个答案:

答案 0 :(得分:50)

函数的arity存储在var。

的元数据中
(:arglists (meta #'str))
;([] [x] [x & ys])

这要求使用defn或明确提供的:arglists元数据定义函数。

答案 1 :(得分:49)

偷偷摸摸的反思:

(defn arg-count [f]
  (let [m (first (.getDeclaredMethods (class f)))
        p (.getParameterTypes m)]
    (alength p)))

或者:

(defn arg-count [f]
  {:pre [(instance? clojure.lang.AFunction f)]}
  (-> f class .getDeclaredMethods first .getParameterTypes alength))

答案 2 :(得分:3)

以@ whocaresanyway的解决方案为基础:

(defn provided
  [cond fun x]
  (if cond
    (fun x)
    x))

(defn append
  [xs x]
  (conj (vec xs) x))

(defn arity-of-method
  [method]
  (->> method .getParameterTypes alength))

(defn arities
  [fun]
  (let [all-declared-methods (.getDeclaredMethods (class fun))
        methods-named (fn [name]
                        (filter #(= (.getName %) name) all-declared-methods))
        methods-named-invoke (methods-named "invoke")
        methods-named-do-invoke (methods-named "doInvoke")
        is-rest-fn (seq methods-named-do-invoke)]
    (->> methods-named-invoke
         (map arity-of-method)
         sort
         (provided is-rest-fn
                   (fn [v] (append v :rest))))))

答案 3 :(得分:2)

我的心脏流血(覆盖所有病例)。

(defn arity
  "Returns the maximum arity of:
    - anonymous functions like `#()` and `(fn [])`.
    - defined functions like `map` or `+`.
    - macros, by passing a var like `#'->`.

  Returns `:variadic` if the function/macro is variadic."
  [f]
  (let [func (if (var? f) @f f)
        methods (->> func class .getDeclaredMethods
                     (map #(vector (.getName %)
                                   (count (.getParameterTypes %)))))
        var-args? (some #(-> % first #{"getRequiredArity"})
                        methods)]
    (if var-args?
      :variadic
      (let [max-arity (->> methods
                           (filter (comp #{"invoke"} first))
                           (sort-by second)
                           last
                           second)]
        (if (and (var? f) (-> f meta :macro))
          (- max-arity 2) ;; substract implicit &form and &env arguments
          max-arity)))))

(use 'clojure.test)

(defmacro m ([a]) ([a b]))
(defmacro mx [])

(deftest test-arity
  (testing "with an anonymous #(… %1) function"
    (is (= 1           (arity #(+ % 32))))
    (is (= 1           (arity #(+ %1 32))))
    (is (= 2           (arity #(+ %1 %2))))
    (is (= 13          (arity #(+ %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 %13))))
    (is (= :variadic   (arity #(apply + %&))))
    (is (= :variadic   (arity #(apply + % %&)))))
  (testing "with an anonymous (fn [] …) function"
    (testing "single body"
      (is (= 0         (arity (fn []))))
      (is (= 1         (arity (fn [a]))))
      (is (= 2         (arity (fn [a b]))))
      (is (= 20        (arity (fn [a b c d e f g h i j k l m n o p q r s t]))))
      (is (= :variadic (arity (fn [a b & more])))))
    (testing "multiple bodies"
      (is (= 0         (arity (fn ([])))))
      (is (= 1         (arity (fn ([a])))))
      (is (= 2         (arity (fn ([a]) ([a b])))))
      (is (= :variadic (arity (fn ([a]) ([a b & c])))))))
  (testing "with a defined function"
    (is (= :variadic   (arity map)))
    (is (= :variadic   (arity +)))
    (is (= 1           (arity inc))))
  (testing "with a var to a macro"
    (is (= :variadic   (arity #'->)))
    (is (= 2           (arity #'m)))
    (is (= 0           (arity #'mx)))))

(run-tests)

答案 4 :(得分:1)

实际上它也适用于宏:

(defn arg-count [f]
  (let [m (first (.getDeclaredMethods (class f)))
        p (.getParameterTypes m)]
    (alength p)))

(defmacro my-macro [])

(arg-count @#'my-macro)
; 2

为什么2?因为每个宏分别有两个隐式参数&form&env

答案 5 :(得分:0)

我对arity问题的看法,建立在其他解决方案的基础上:

(defn arity
 "Returns the maximum parameter count of each invoke method found by refletion
  on the input instance. The returned value can be then interpreted as the arity
  of the input function. The count does NOT detect variadic functions."
  [f]
  (let [invokes (filter #(= "invoke" (.getName %1)) (.getDeclaredMethods (class f)))]
  (apply max (map #(alength (.getParameterTypes %1)) invokes))))

答案 6 :(得分:-18)

user=> (defn test-func
         ([p1] "Arity was 1.")
         ([p1 p2] "Arity was 2.")
         ([p1 p2 & more-args] (str "Arity was " (+ 2 (count more-args)))))
#'user/test-func
user=> (test-func 1)
"Arity was 1."
user=> (test-func 1 2)
"Arity was 2."
user=> (test-func 1 2 3)
"Arity was 3"
user=> (test-func 1 2 3 4)
"Arity was 4"
user=> (test-func 1 2 3 4 5) ;...
"Arity was 5"