2012-04-25 25 views
17

Tôi có một số mã sử dụng nhiều phương pháp và lý tưởng là quá tải chức năng (trong trường hợp này là đa chức năng) để tôi có thể vượt qua trong một hàm bậc cao hơn để giúp thử nghiệm, ví dụ.Có thể quá tải Clojure đa phương pháp trên tinh thần không?

Dưới đây là ví dụ:

(ns multi) 

(defn my-print [m] (println "The colour is" (:colour m))) 

(defmulti which-colour-mm (fn [m f] (:colour m))) 

(defmethod which-colour-mm :blue [m f] (f m)) 
(defmethod which-colour-mm :red [m f] (f m)) 
(defmethod which-colour-mm :default [m f] (println "Default: Neither Blue nor Red")) 

(defn which-colour 
    ([m] (which-colour-mm m my-print)) 
    ([m f] (which-colour-mm m f))) 

(which-colour {:colour :blue :object :ball}) 
(which-colour {:colour :yellow :object :ball}) 
(which-colour {:colour :blue :animal :parrot} (fn [m] (println "The " (:animal m) "is" (:colour m)))) 

Vì vậy defn tôi cung cấp các arity quá tải nhưng tôi tự hỏi nếu defmethod hỗ trợ bất cứ điều gì như thế này. (Tôi đoán bạn sẽ không muốn làm điều đó cho từng tờ khai defmethod.)

Đây có phải là thích hợp nhất (tôi dám nói, ngữ) cách tiếp cận, hoặc là có một cách tốt hơn?

Trả lời

14

Điều này hoàn toàn ổn. Có giao diện "người dùng" và giao diện "kiểu" của một thư viện. Chúng có thể giống hệt nhau, nhưng chúng không phải như vậy.

Giao diện "người dùng" nằm trong trường hợp của bạn which-colour. Giao diện "loại" là which-colour-mm (ok, không thực sự, nhưng chỉ vì lợi ích của đối số). Người sử dụng thư viện của bạn không cần biết về multimethod.

Mặt khác, ai đó đang cung cấp một màu mới - giả sử :purple - không phải quan tâm đến bản mẫu nhiều người. Điều này được xử lý cho anh ta trong which-colour.

Đây là thiết kế hoàn toàn hợp lệ! Nhưng tất nhiên có một thẻ giá: Giả sử bạn có một màu sắc, trong đó có một số cách nhiều hơn để làm việc perfomant ... Bây giờ, bạn đang bị khóa vào một giao diện có thể chậm hơn. Chúc vui vẻ!

Để làm rõ điều này một chút: Giả sử bạn có giao diện bộ sưu tập. Bạn cung cấp chức năng - conj - cho phép người dùng thêm các phần tử vào bộ sưu tập. Nó được thực hiện như thế này:

(defn conj 
    [coll & elements] 
    (reduce conj1 coll elements)) 

conj1 là "loại" giao diện (ví dụ như một multimethod hoặc giao thức chức năng.): Nó bổ sung thêm một yếu tố để các bộ sưu tập. Vì vậy, ai đó cung cấp một loại bộ sưu tập mới chỉ để thực hiện trường hợp đơn giản thêm một đối số duy nhất. Và automagically loại mới cũng sẽ hỗ trợ thêm nhiều yếu tố.

Nhưng bây giờ giả sử bạn có một loại bộ sưu tập, cho phép cách nhanh hơn để thêm nhiều yếu tố hơn là chỉ thêm một phần tử. Không thể sử dụng khả năng này ngay bây giờ.

Vì vậy, bạn thực hiện chức năng đa phương thức/giao thức với hàm conj. Bây giờ bộ sưu tập có thể sử dụng cách nhanh hơn. Nhưng mỗi lần triển khai phải cung cấp nhiều phần tử boilerplate.

Đây là sự cân nhắc và quyết định của bạn. Không có Way Right (tm). Cả hai đều có thể được coi là thành ngữ. (Mặc dù cá nhân tôi sẽ cố gắng đi với người đầu tiên càng thường xuyên càng tốt.)

YMMV.

Chỉnh sửa: Ví dụ về các phương pháp đa dạng không có mã hóa trong giá trị công văn.

(defmulti which-colour-mm (fn [m & args] (:colour m))) 
(defmethod which-colour-mm :blue 
    ([m] (print m)) 
    ([m f] (f m))) 
+0

tôi thích video này và câu trả lời Ankur, nhưng một này sử dụng arity quá tải v s khác mà sử dụng các đối số đếm để phù hợp với giá trị công văn. Tôi đoán nó có ý nghĩa để sử dụng cách tiếp cận defn nếu bạn muốn cùng một chức năng mặc định cho mỗi giá trị công văn (và tránh trùng lặp) vs quá tải ở mức defmethod nếu bạn muốn một mặc định khác nhau cho mỗi giá trị công văn. –

3

Bạn có thể làm điều đó bằng multimethods như hình dưới đây Ví dụ:

(defmulti which-colour-mm (fn [m & args] [(count args) (:colour m)])) 
(defmethod which-colour-mm [0 :blue] [m] (print m)) 
(defmethod which-colour-mm [1 :blue] [m f] (f m)) 


user=> (which-colour-mm {:colour :blue :object :ball}) 
{:colour :blue, :object :ball}nil 
user=> (which-colour-mm {:colour :blue :object :ball} print) 
{:colour :blue, :object :ball}nil 
2

Về cơ bản bạn có thể gửi vào bất cứ điều gì, không phải loại lẫn số args phải là consistent..like này:

(defn- map-classes [an-object] 
    (let [cmap 
     {1 :thing 
      2 666 
      3 "yada"} 
    the-class (class an-object)] 
    (get cmap an-object the-class))) 

(defn- mk-class [& args] (map #(map-classes %) args)) 
(defmulti play-thing mk-class) 
(defmethod play-thing [:thing] [v] (= 1 v)) 
(defmethod play-thing [666] [v] (= 2 v)) 
(defmethod play-thing ["yada" String] [v x] (str x v)) 

Các khả năng là vô tận

Các vấn đề liên quan