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

Gab*_*iMe 47 functional-programming clojure

给定一个函数对象或名称,我该如何确定它的arity?有点像(arity func-name).

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

Mik*_*las 51

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

(:arglists (meta #'str))
;([] [x] [x & ys])
Run Code Online (Sandbox Code Playgroud)

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

  • 核心职能属于"定义"范畴.例如(def my-identity1(fn [x] x))将不起作用,而(defn my-identity2 [x] x)将起作用.defn sets:元数据中的arglists.核心功能可以手动使用defn或set:arglists.注意:它也*不适用于多方法.你必须在那里手动设置:arglists :( defmulti my-method {:arglists'([foo bar])}(fn [&args](vec(map type args)))).所以依赖:arglists充其量是有缺陷的. (6认同)
  • 请注意,这实际上仅适用于使用defn定义的函数.它不适用于使用fn或#()定义的匿名函数. (2认同)

小智 50

偷偷摸摸的反思:

(defn arg-count [f]
  (let [m (first (.getDeclaredMethods (class f)))
        p (.getParameterTypes m)]
    (alength p)))
Run Code Online (Sandbox Code Playgroud)

要么 :

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

  • 这似乎是匿名函数的唯一选择,但我不喜欢假设第一个声明的方法是invoke方法.我会改变`(first(.getDeclaredMethods(class f)))`而不是`(first(filter#(="invoke"(.getName%))(.getDeclaredMethods(class f))))` (2认同)

小智 6

我的心在流血(涵盖所有情况)。

\n\n
(defn arity\n  "Returns the maximum arity of:\n    - anonymous functions like `#()` and `(fn [])`.\n    - defined functions like `map` or `+`.\n    - macros, by passing a var like `#\'->`.\n\n  Returns `:variadic` if the function/macro is variadic."\n  [f]\n  (let [func (if (var? f) @f f)\n        methods (->> func class .getDeclaredMethods\n                     (map #(vector (.getName %)\n                                   (count (.getParameterTypes %)))))\n        var-args? (some #(-> % first #{"getRequiredArity"})\n                        methods)]\n    (if var-args?\n      :variadic\n      (let [max-arity (->> methods\n                           (filter (comp #{"invoke"} first))\n                           (sort-by second)\n                           last\n                           second)]\n        (if (and (var? f) (-> f meta :macro))\n          (- max-arity 2) ;; substract implicit &form and &env arguments\n          max-arity)))))\n\n(use \'clojure.test)\n\n(defmacro m ([a]) ([a b]))\n(defmacro mx [])\n\n(deftest test-arity\n  (testing "with an anonymous #(\xe2\x80\xa6 %1) function"\n    (is (= 1           (arity #(+ % 32))))\n    (is (= 1           (arity #(+ %1 32))))\n    (is (= 2           (arity #(+ %1 %2))))\n    (is (= 13          (arity #(+ %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 %13))))\n    (is (= :variadic   (arity #(apply + %&))))\n    (is (= :variadic   (arity #(apply + % %&)))))\n  (testing "with an anonymous (fn [] \xe2\x80\xa6) function"\n    (testing "single body"\n      (is (= 0         (arity (fn []))))\n      (is (= 1         (arity (fn [a]))))\n      (is (= 2         (arity (fn [a b]))))\n      (is (= 20        (arity (fn [a b c d e f g h i j k l m n o p q r s t]))))\n      (is (= :variadic (arity (fn [a b & more])))))\n    (testing "multiple bodies"\n      (is (= 0         (arity (fn ([])))))\n      (is (= 1         (arity (fn ([a])))))\n      (is (= 2         (arity (fn ([a]) ([a b])))))\n      (is (= :variadic (arity (fn ([a]) ([a b & c])))))))\n  (testing "with a defined function"\n    (is (= :variadic   (arity map)))\n    (is (= :variadic   (arity +)))\n    (is (= 1           (arity inc))))\n  (testing "with a var to a macro"\n    (is (= :variadic   (arity #\'->)))\n    (is (= 2           (arity #\'m)))\n    (is (= 0           (arity #\'mx)))))\n\n(run-tests)\n
Run Code Online (Sandbox Code Playgroud)\n


mis*_*tor 5

基于@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))))))
Run Code Online (Sandbox Code Playgroud)