2009-11-08 14 views

risposta

48

L'arity di una funzione è memorizzato nei metadati del var.

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

Ciò richiede che la funzione era o definita utilizzando defn, o le :arglists metadati forniti esplicitamente.

+0

Superbo. Grazie mille – GabiMe

+2

Nota, questo funziona davvero solo per le funzioni definite con defn. Non funziona per le funzioni anonime definite con fn o #(). – kotarak

+0

Per quanto posso vedere funziona anche per tutte le funzioni incorporate. Ad esempio (: arglists (meta # '+)) o (: arglists (meta #' println)) le funzioni principali – GabiMe

-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" 
+5

Non voglio * eseguire * la funzione per conoscerne l'appartenenza. E non voglio cambiare il codice funzione per questo – GabiMe

47

riflessione subdolo:

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

Oppure:

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

Non funziona su macro – GabiMe

+13

Ma funziona su funzioni anonime. +1 –

+2

Questa sembra essere l'unica opzione per le funzioni anonime, ma non mi piace il presupposto che il primo metodo dichiarato sia il metodo invoke. Io cambierei '(prima (.getDeclaredMethods (classe f)))' invece sarà '(prima (filtro # (=" invoca "(.getName%)) (.getDeclaredMethods (classe f))))' –

3

Sulla base di @ whocaresanyway soluzione:

(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)))))) 
0

mio prendere il problema arità, basandosi sui altre soluzioni:

(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)))) 
1

In realtà funziona anche sulle macro:

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

(defmacro my-macro []) 

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

Perché 2? Perché ogni macro ha due argomenti impliciti &form e &env rispettivamente.

0

Il mio cuore sanguinava (copriva tutti i casi).

(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) 
Problemi correlati