Câu hỏi Làm thế nào tôi có thể xác định số lượng đối số cho một hàm trong Clojure?


Cho một hàm x trong clojure làm thế nào tôi có thể lập trình nhận số đếm đối số?

ví dụ:

(fn a [b c] ...) has has two arguments
(fn a [] ...) has has zero arguments

15
2018-06-19 17:56


gốc


Hãy xem ở đây có thể: clojure.github.io/clojure/clojure.reflect-api.html - BlackBear
Tại sao bạn muốn làm nó? - mange
Khi tôi muốn gọi một phương thức mà someones có 0 và đôi khi có 1 đối số - Zubair
Trường hợp sử dụng của tôi cho mục này là các mục 'biting off' (tiêu thụ) từ một chuỗi bằng cách sử dụng các hàm có số lượng đối số khác nhau. (Trình tự cung cấp các giá trị không được sử dụng hai lần.) - 0dB


Các câu trả lời:


Nếu bạn có quyền truy cập vào var chứa hàm bạn có thể lấy số đối số bằng cách truy cập siêu dữ liệu của nó theo cách sau:

(defn arities [v]
  (->> v meta :arglists (map count)))

(defn a [])
(defn b [_ _])

(map arities [#'a #'b])
;= ((0) (2))

arities sẽ trả về một seq với tất cả các vị trí cho hàm. Điều này có bất lợi cho một vectơ đối số variadic ([_ _ & _]) nó sẽ trở lại (4).

(defn c [_ _ & _])
(arities #'c)
;= (4)

Điều này có thể được khắc phục bằng cách xóa & biểu tượng từ tất cả các danh sách đối số.

(defn arities [v]
  (->> v 
    meta 
    :arglists 
    (map #(remove #{'&} %))
    (map count)))

(arities #'c)
;= (3)

Nếu bạn không có quyền truy cập vào var, sau đây là một hàm nhỏ mà tôi đã sử dụng để phát hiện số đối số của một hàm. Nó sử dụng sự phản chiếu vì vậy nó không phải là cách tiếp cận bạn có thể muốn thực hiện nếu bạn cần hiệu suất tốt. Cũng tính đến việc nó dựa vào chi tiết triển khai.

(defn n-args [f]
  (-> f class .getDeclaredMethods first .getParameterTypes alength))

(defn a [])
(defn b [_ _])
(defn c [_ _ & _])

(map n-args [a b c])
;= (0 2 3)

CHỈNH SỬA

Sau khi đưa ra một câu trả lời khác đọc, tôi nhận ra kết quả 3 cho một hàm variadic được định nghĩa là (defn x [_ _ & _] ,,,), thực sự là khá gây hiểu lầm vì nó là kết quả tương tự bạn sẽ nhận được cho một hàm với 3 đối số. Phiên bản sau sẽ trả về :variadic, thay vì một số cụ thể, đối với các vectơ đối số có chứa & biểu tượng (trừ trường hợp [&] Ở đâu & đó là tên đối số thực tế). Như đã đề cập trong nhận xét của Jeremy Heiler việc tính số đối số từ siêu dữ liệu chỉ hoạt động nếu giá trị cho :arglists không được thay đổi theo cách thủ công.

(defn a [_])
(defn b [_ _])
(defn c [_ _ & _])
(defn d [_ _ _])
(defn e [&])

(defn variadic? [s]
  (and (some #{'&} s)
       (not (every? #{'&} s))))

(defn arities [v]
  (->> v
    meta
    :arglists
    (map #(if (variadic? %) :variadic %))
    (map #(if (sequential? %) (count %) %))))

(map arities [#'a #'b #'c #'d #'e])
;= ((1) (2) (:variadic) (3) (:variadic))

Phiên bản phản chiếu cho điều này phức tạp hơn một chút và dựa vào các chi tiết triển khai thực hiện nhiều hơn (nghĩa là "Hàm này hay hàm đó được khai báo?" Hoặc "Hàm này có mở rộng lớp X không?"), Vì vậy tôi sẽ không khuyên bạn sử dụng phương pháp đó .


13
2018-06-19 18:27



Tôi vẫn đang làm việc này. Tôi đã không nhận ra nó quá phức tạp ... tôi đã nghĩ rằng một ngôn ngữ năng động như vậy đã dễ dàng truy cập vào danh sách đối số - Zubair
Cần lưu ý rằng: argslist có thể được ghi đè theo cách thủ công: (defn baz {:arglists '([boom])} [a b]) sẽ cho kết quả (:arglists (meta #'baz)) ;=> ([boom]) - Jeremy
Tôi đề cập đến điều này không chỉ vì nó có thể xảy ra, nhưng nó làm xảy ra. Bạn sẽ thấy nó rất nhiều trong clojure.core trong các hàm được xác định trước defn. Ngoài ra, nó được sử dụng để làm rõ cấu trúc của các đối số biến. (Ví dụ, đối số tùy chọn cho defn.) - Jeremy
Một cảnh báo khác: Nói (arities #'f1) sản lượng (2). Bây giờ tạo một var mới cho cùng một hàm bằng cách sử dụng (def f2 f1). Nhưng (arities #'f2) evals to () và không (2) nữa. - 0dB


Bạn có thể xây dựng một hàm lấy tất cả các đối số, đặt chúng vào một danh sách và trả về số đếm như sau:

(defn arg-count [& rest] (count rest))
(arg-count) ;; 0
(arg-count 1 2 3) ;; 3

0
2018-06-19 20:16



Tôi nghĩ rằng OP muốn có thể khám phá ra sự tinh khiết của bất kỳ chức năng tùy ý nào. - Alex
Điều này chỉ hoạt động cho các đối số độ dài thay đổi, nhưng hãy thử tốt - Zubair