2011-09-20 29 views
32

Lisp thông dụng có return-from; có bất kỳ loại nào của return trong Clojure khi bạn muốn quay lại sớm từ một hàm không?Làm thế nào để bạn quay trở lại từ một hàm sớm ở Clojure?

+5

Clojure mã có xu hướng không được cấu trúc như một loạt các báo cáo, vì vậy trở về đầu không nhất thiết phải là có ý nghĩa . Nếu bạn muốn đưa ra ví dụ về nơi bạn muốn, mọi người có thể đề xuất các lựa chọn thay thế. – Chuck

Trả lời

20

Không có bất kỳ câu lệnh trả lại rõ ràng nào trong clojure. Bạn có thể hack một cái gì đó với nhau bằng cách sử dụng một kết hợp catch/throw nếu bạn muốn, nhưng kể từ khi clojure là nhiều chức năng hơn lisp thông thường, cơ hội bạn thực sự cần một quyền trả lại sớm ở giữa của một số khối lồng nhau là nhỏ hơn nhiều so với CL. Lý do duy nhất 'tốt' mà tôi có thể thấy cho báo cáo trả về là khi bạn đang xử lý các đối tượng có thể thay đổi theo cách không phải là thành ngữ trong clojure.

Tôi sẽ không đi xa như nói rằng nó không bao giờ hữu ích, nhưng tôi nghĩ rằng trong Clojure, nếu thuật toán của bạn cần một câu trả về, đó là một mùi mã chính.

+3

Cảm ơn mẹo. Ví dụ cụ thể: Khi phân tích cú pháp dòng lệnh args, tôi muốn kiểm tra xem mỗi tùy chọn có hợp lệ hay không. Lúc đầu, tôi đã thử kiểm tra từng tùy chọn, nếu một tùy chọn không hợp lệ, sau đó tôi muốn trả lại một thông báo hữu ích cho người dùng trước khi tiếp tục với việc xác thực phần còn lại của các tùy chọn. Sau khi thực hiện trở lại ở giữa chức năng là một mùi mã, tôi đã viết một "xác nhận" chức năng trả về một danh sách các tin nhắn (một tin nhắn cho mỗi giá trị tùy chọn không hợp lệ). Nếu danh sách thư được trả về bằng cách xác thực là trống, thì tất cả các tùy chọn đều được xác thực thành công. Đẹp hơn nhiều sau khi refactor! – Upgradingdave

0

Tóm lại, không. Nếu đây là một vấn đề thực sự đối với bạn thì bạn có thể giải quyết vấn đề này với "the maybe monad" Monads có số mức cao trí tuệ cao vì vậy trong nhiều trường hợp, Clojurians có xu hướng tránh kiểu lập trình "nếu không quay trở lại".

Nó giúp phá vỡ chức năng thành các chức năng nhỏ hơn để giảm ma sát từ điều này.

13

Tôi không có chuyên gia về Clojure, nhưng có vẻ như nó không có những cấu trúc đó để cố gắng hoạt động hiệu quả hơn. Hãy nhìn vào những gì Stuart Halloway nói here:

Common Lisp cũng hỗ trợ quay trở lại-từ vĩ mô để "trở lại" từ giữa một hàm. Điều này khuyến khích một phong cách bắt buộc của chương trình , mà Clojure không khuyến khích.

Tuy nhiên, bạn có thể giải quyết các vấn đề tương tự theo cách khác. Dưới đây là sự trở lại-từ ví dụ, viết lại theo một phong cách chức năng để không trở lại-từ là cần thiết:

(defn pair-with-product-greater-than [n] 
(take 1 (for [i (range 10) j (range 10) :when (> (* i j) n)] [i j]))) 

Đó là, sử dụng chuỗi lười biếng và các giá trị trở lại dựa trên điều kiện.

17

Trừ khi bạn đang viết một số mã thực sự sôi nổi, lý do duy nhất bạn muốn quay trở lại sớm là nếu một số điều kiện được đáp ứng. Nhưng vì các hàm luôn trả về kết quả của biểu mẫu cuối cùng được đánh giá, if đã là hàm này - chỉ cần đặt giá trị mà bạn muốn trả về trong phần thân của if và nó sẽ trả về giá trị đó nếu điều kiện được đáp ứng.

0

Tùy chọn if đã được đưa ra có lẽ là lựa chọn tốt nhất và lưu ý vì bản đồ dễ dàng, bạn luôn có thể trả lại {:error "blah"} trong điều kiện lỗi và {result: x} trong điều kiện hợp lệ.

0

Không có câu trả lời trong Clojure. Ngay cả khi bạn chọn không thực thi một số mã bằng cách sử dụng cấu trúc luồng như if hoặc when, hàm sẽ luôn trả về một cái gì đó, trong các trường hợp này là nil. Cách duy nhất ra là ném một ngoại lệ, nhưng thậm chí sau đó nó sẽ hoặc là bong bóng tất cả các con đường lên và giết chủ đề của bạn, hoặc bị bắt bởi một chức năng - mà sẽ trả về một giá trị.

19

Khi bạn cần bảo lãnh sớm tính toán, bạn cần có cách để thực hiện điều đó, không phải là đối số từ người thuần túy.Thông thường bạn cần nó khi bạn đang giảm một bộ sưu tập lớn và một giá trị nhất định cho thấy rằng không có điểm xử lý tiếp theo bộ sưu tập. Để kết thúc, Clojure luôn thực tế cung cấp chức năng reduced. Một ví dụ đơn giản để minh họa là khi nhân một chuỗi các số, nếu bạn gặp một số không, bạn đã biết rằng kết quả cuối cùng sẽ bằng không, vì vậy bạn không cần phải nhìn vào phần còn lại của chuỗi. Đây là cách bạn viết mã rằng với reduced:

(defn product [nums] 
    (reduce #(if (zero? %2) 
       (reduced 0.0) 
       (* %1 %2)) 
      1.0 
      nums)) 

reduced kết thúc tốt đẹp các giá trị mà bạn cung cấp cho nó trong một cấu trúc dữ liệu trọng điểm để reduce biết dừng lại đọc từ bộ sưu tập và chỉ cần trả về giá trị reduced ngay bây giờ. Hey, nó tinh khiết, thậm chí!

Bạn có thể xem những gì đang xảy ra nếu bạn quấn trên if trong một do với một (println %1 %2):

user=> (product [21.0 22.0 0.0 23.0 24.0 25.0 26.0]) 
1.0 21.0 
21.0 22.0 
462.0 0.0 
0.0 

user=> (product [21.0 22.0 23.0 24.0 25.0 26.0]) 
1.0 21.0 
21.0 22.0 
462.0 23.0 
10626.0 24.0 
255024.0 25.0 
6375600.0 26.0 
1.657656E8 
+5

Đây phải là câu trả lời đúng. "Khi bạn cần phải bảo lãnh sớm tính toán, bạn cần một cách để làm điều đó, không phải là một đối số từ những người thuần túy". Hiệu quả luôn luôn là phải. Giai đoạn. Ví dụ về một hàm để kiểm tra xem một id có được sao chép trong một tập bản đồ hay không: (defn duplicate? [Id myset] (reduce (fn [av] (if (= (: id v) id) (giảm true) false)) false myset)) – Chocksmith

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