2012-01-11 28 views
12

Tôi đã học Clojure và bối rối bởi những điều sau đây:Khó hiểu: Clojure cho vòng lặp với: while -> hành vi bất ngờ?

user=> (for [a (range 1 4) b (range 1 4)] [a b]) 
([1 1] [1 2] [1 3] [2 1] [2 2] [2 3] [3 1] [3 2] [3 3]); _no surprise here_ 

Hãy thêm :while (not= a b), tôi mong đợi để xem một danh sách rỗng như vòng lặp nên dừng lại nếu điều kiện là sai. Trong trường hợp này, đây là mục đầu tiên trong đó a = b = 1. Hãy xem:

user=> (for [a (range 1 4) b (range 1 4) :while (not= a b) ] [a b]) 

([2 1] [3 1] [3 2]) ; _surprise!_ 

Thay đổi :while-:when để lọc ra (= a b) cặp

user=> (for [a (range 1 4) b (range 1 4) :when (not= a b) ] [a b]) 
([1 2] [1 3] [2 1] [2 3] [3 1] [3 2]); _expected_ 

bất cứ ai có thể giải thích lý do tại sao (for [ ... :while ..] ...) cư xử như thế này?

Tôi đang sử dụng Clojure 1.3 trên OS X.

Cảm ơn bạn và xin lỗi vì thiếu định dạng. Đây là bài đăng trinh của tôi trên StackOverflow.

Trả lời

9

Hãy xem từng lần lặp lại.

a = 1 
    b = 1 -> a == b, break because of while 

a = 2 
    b = 1 -> a != b, print [2 1] 
    b = 2 -> a == b, break because of while 

a = 3 
    b = 1 -> a != b, print [3 1] 
    b = 2 -> a != b, print [3 2] 
    b = 3 -> a == b, break because of while 
+0

Cảm ơn bạn, Nikita. Vì vậy, nếu: trong khi chỉ áp dụng cho vòng lặp bên trong, làm thế nào để làm cho nó áp dụng cho vòng lặp bên ngoài là tốt? Vấn đề tôi có là: (đối với [từ [: a: b: c: d: e: f] thành [: a: b: c: d: e: f]: cho phép [đường dẫn (tìm đường dẫn ab)] : trong khi đường dẫn) đường dẫn); vòng lặp nên dừng khi đường dẫn là 0. – jbear

+0

Xin lỗi, ý tôi là (đối với [từ [: a: b: c: d: e: f] thành [: a: b: c: d: e: f]: cho phép [đường dẫn (tìm đường dẫn đến) ]: trong khi đường dẫn] đường dẫn) – jbear

+0

@ jbear, tôi không biết: (Có thể là bạn nên tạo ra các đường dẫn lười biếng và đi trong khi chúng không rỗng. Tôi không chắc chắn rằng 'for' là lười biếng. –

3

Điều kiện :while trong for chỉ chấm dứt vòng lặp bên trong nhất. Tôi sử dụng for tất cả các thời gian, nhưng :while vì vậy hiếm khi mà tôi không bao giờ nhận ra điều này; cảm ơn vì câu hỏi tuyệt vời của bạn!

Đáng buồn là tôi nghĩ tốt nhất bạn có thể làm là quấn take-while xung quanh for, vì bạn muốn một bộ đếm dừng "toàn cầu" trên chuỗi đầu ra, chứ không phải bộ đếm dừng trên một trong các chuỗi đầu vào mà bạn đang lặp lại kết thúc. Ví dụ:

(->> (for [a (range 1 4) 
      b (range 1 4)] 
     [a b]) 
    (take-while (fn [[a b]] (not= a b)))) 

() 
+1

Chỉ cần rõ ràng, kiểm tra': while' áp dụng cho chuỗi ngay trước nó, không nhất thiết phải là vòng lặp bên trong nhất. –

+0

Cảm ơn vì đã làm rõ, Alex. Cho sự nổi bật của ngôn ngữ, Tôi không thể không cảm thấy hơi thất vọng khi một trong những khối xây dựng cơ bản - _list comprehension_ - nên trở nên khá hạn chế. Hy vọng rằng khi việc học của tôi tiến triển, tôi sẽ khám phá nhiều cách tốt hơn để làm điều đó, với sự trợ giúp tuyệt vời – jbear

+0

@jbear Nó thực sự ít hạn chế theo cách này hơn là nó uld là cách khác. Bằng cách này, bạn có thể dễ dàng bọc một biểu thức 'for' trong' take-while' nếu bạn muốn hành vi "toàn cầu". Nhưng nếu hành vi "toàn cầu" là những gì 'for /: while' đã làm, bạn sẽ lấy lại hành vi" trung gian "hiện tại như thế nào? Bạn sẽ phải từ bỏ 'for' hoàn toàn và xây dựng một tổ chuột của bản đồ, mapcat, bộ lọc, mất trong khi. – amalloy

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