2009-11-08 5 views

답변

48

함수의 인수에 대응이 VAR의 메타 데이터에 저장되어 꽤 중심이기 때문에

내가하는 방법이 있기를 바랍니다.

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

이 함수이었다 defn하거나 또는 명시 적으로 제공된 :arglists 메타 데이터를 사용하여 정의하는 것이 필요하다.

+0

최고. 고마워요. – GabiMe

+2

참고 : 이것은 실제로 defn으로 정의 된 함수에서만 작동합니다. fn 또는 #()로 정의 된 익명의 함수에는 작동하지 않습니다. – kotarak

+0

필자가 볼 수있는 한, 모든 내장 함수에서도 작동합니다. 예를 들어 (: arglists (meta # '+)) 또는 (: arglists (meta #'println)) – 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

나는 그 기능을 알기 위해 * 함수를 * 실행하고 싶지 않습니다. 그리고이 기능 코드를 변경하고 싶지 않습니다. – GabiMe

47

음흉 반사 :

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

매크로에서 작동하지 않습니다 – GabiMe

+13

하지만 익명 함수에서 작동합니다. +1 –

+2

이것은 익명 함수에 대한 유일한 옵션 인 것 같지만 처음 선언 된 메서드가 invoke 메서드라는 가정이 마음에 들지 않습니다. 내가 대신 '(첫 번째 (필터 # (= "호출"(.getName %)) (.getDeclaredMethods (클래스 f))))''(첫 번째 (.getDeclaredMethods (클래스 f) –

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

인수에 대응 문제에서 필자의 다른 솔루션 구축 :

1

(10)는 사실 또한 매크로에서 작동 :

(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이 각각 있기 때문입니다.

0

내 심장이 피었습니다 (모든 경우 포함).

(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) 
관련 문제