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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Clojure:如何在运行时找出函数的arity?相关的知识,希望对你有一定的参考价值。

给定一个函数对象或名称,我该如何确定它的arity?像(arity func-name)之类的东西。

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

答案

函数的arity存储在var的元数据中。

(:arglists (meta #'str))
;([] [x] [x & ys])

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

另一答案

偷偷摸摸的反思:

(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))
另一答案

基于@ 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))))))
另一答案

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

(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)
另一答案

实际上它也适用于宏:

(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

另一答案

我对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))))
另一答案
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"

以上是关于Clojure:如何在运行时找出函数的arity?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Clojure 中对函数进行基准测试?

如何使clojure程序结构更容易识别?

Clojure:使用来自 REPL 的库函数

如何在 Clojure 中创建具有负指数的幂函数?

什么是Clojure内在函数

Clojure 合并函数