2009-04-17 31 views
24

Trong khi sử dụng std :: for_each thuật toán làm thế nào để tôi phá vỡ khi một điều kiện nhất định được hài lòng?Breaking in std :: for_each loop

+0

Cá nhân tôi không thích thoát khỏi vòng lặp sớm. IMO, cho vòng lặp chỉ được sử dụng khi bạn cần lặp lại đầy đủ. Nếu bạn muốn đột nhập vào giữa, hãy xem xét các cấu trúc lặp khác. –

+4

Hãy thử: std :: find_if –

Trả lời

10

Bạn có thể sử dụng thuật toán find_if, sẽ dừng và trả về trình lặp trong đó điều kiện biến vị ngữ được áp dụng cho phần tử lặp được trả về true. Vì vậy, vị từ của bạn nên được thay đổi để trả về một boolean là điều kiện tiếp tục/break.

Tuy nhiên, đây là một hack, vì vậy bạn có thể sử dụng các thuật toán.

Một cách khác là sử dụng BOOST_FOREACH.

+0

Có, tôi có thể làm điều đó, nhưng nếu tôi muốn áp dụng một số thao tác cho các phần tử trong vòng lặp cho đến khi điều kiện được thỏa mãn? – Naveen

+0

Naveen, có chuyện gì vậy? bạn có thể thực hiện bất kỳ thao tác nào trên các phần tử. sau đó bất cứ lúc nào bạn có thể "phá vỡ;" và vòng lặp thoát. –

+0

find_if chấp nhận các trình vòng lặp đầu vào, không phải các trình vòng lặp đầu ra. Sau đó, tôi khuyên bạn nên thực hiện vòng lặp bằng tay hoặc sử dụng BOOST_FOREACH. –

2

Bạn không thể làm điều đó, trừ khi bạn ném một ngoại lệ, đó không phải là một ý tưởng tốt bởi vì bạn không kiểm soát luồng với ngoại lệ.

Cập nhật: dường như Boost có for_each_if rằng có thể trợ giúp nhưng bạn không sử dụng Boost.

+0

bạn không cần tăng cường để sử dụng for_each_if(). Nó rất đơn giản để thực hiện. –

+0

Đúng, nhưng câu hỏi không phải là "làm thế nào để tôi thực hiện for_each_if()". –

+0

for_each_if cũng không giải quyết được câu hỏi. Nó không phải là "làm thế nào để tôi làm cho mã của tôi không làm gì trên các phần tử sau một thời điểm nhất định", nhưng "làm thế nào để dừng hoàn toàn lặp lại". Có thể có một số chi phí hiệu năng nghiêm trọng để vượt qua toàn bộ phạm vi nếu việc lặp lại có thể đã bị chấm dứt trước đó. Vì vậy, câu trả lời là ngoại lệ hoặc không có gì. (nơi không có gì có nghĩa là "thiết kế lại để bạn không phải phá vỡ, hoặc không sử dụng for_each cho điều này".) – jalf

13

Bạn có thể thoát khỏi for_each() bằng cách ném ngoại lệ từ functor của bạn. Tuy nhiên, đây không phải là một ý kiến ​​hay và có những lựa chọn thay thế.

Bạn có thể giữ trạng thái trong hàm của bạn. Nếu bạn phát hiện điều kiện 'break', chỉ cần đặt một lá cờ trong functor của bạn và sau đó cho mỗi lần lặp tiếp theo, bạn chỉ cần quay trở lại mà không cần thực hiện điều gì của functor. Rõ ràng điều này sẽ không dừng lặp lại, mà có thể tốn kém cho các bộ sưu tập lớn, nhưng ít nhất nó cũng sẽ dừng công việc được thực hiện.

Nếu bộ sưu tập của bạn được sắp xếp, bạn có thể tìm thấy() phần tử bạn muốn ngắt, sau đó thực hiện for_each từ đầu() đến phần tử find() trả về.

Cuối cùng, bạn có thể triển khai for_each_if(). Điều này một lần nữa sẽ không dừng lặp lại nhưng sẽ không đánh giá hàm functor của bạn mà công việc đó nếu vị từ ước tính là false. Dưới đây là 2 hương vị của for_each_xxx(), một trong số đó có một giá trị và thực hiện công việc nếu toán tử ==() ước lượng là true và một hàm khác nhận hai functors; một trong đó thực hiện một so sánh ala find_if(), và cái kia thực hiện công việc nếu toán tử so sánh đánh giá là true.

/* --- 

    For each 
    25.1.1 

     template< class InputIterator, class Function, class T> 
      Function for_each_equal(InputIterator first, InputIterator last, const T& value, Function f) 

     template< class InputIterator, class Function, class Predicate > 
      Function for_each_if(InputIterator first, InputIterator last, Predicate pred, Function f) 

    Requires: 

     T is of type EqualityComparable (20.1.1) 

    Effects:  

     Applies f to each dereferenced iterator i in the range [first, last) where one of the following conditions hold: 

      1: *i == value 
      2: pred(*i) != false 

    Returns:  

     f 

    Complexity: 

     At most last - first applications of f 

    --- */ 

    template< class InputIterator, class Function, class Predicate > 
    Function for_each_if(InputIterator first, 
         InputIterator last, 
         Predicate pred, 
         Function f) 
    { 
     for(; first != last; ++first) 
     { 
      if(pred(*first)) 
       f(*first); 
     } 
     return f; 
    }; 

    template< class InputIterator, class Function, class T> 
    Function for_each_equal(InputIterator first, 
          InputIterator last, 
          const T& value, 
          Function f) 
    { 
     for(; first != last; ++first) 
     { 
      if(*first == value) 
       f(*first); 
     } 
     return f; 
    }; 
+0

Bạn có thể phá vỡ bằng cách ném một ngoại lệ. Nhưng giải pháp find() tốt hơn trong một số trường hợp ít nhất là – jalf

+0

True; đã chỉnh sửa phản hồi để kết hợp đầu vào khác. –

+0

Tôi upvoted câu trả lời này sau khi chỉ đọc mã, nhưng tôi phải nói rằng ném một ngoại lệ chỉ để phá vỡ một vòng đầu không bao giờ là một ý tưởng tốt, trừ khi bạn thực sự cần phải ném nếu điều kiện nắm giữ. –

0

Bạn ném ngoại lệ. Có hay không đó là một ý tưởng hay là sắp xếp một câu hỏi về phong cách, tốc độ @Dan, nhưng có thể có nhiều vấn đề với thiết kế của bạn hơn. for_each được thiết kế cho một loại kiểu lập trình chức năng, ngầm định giả định rằng hàm của bạn có thể được áp dụng thống nhất trên toàn bộ tập hợp. Vì vậy, nếu bạn làm cần phải ngắt, điều đó có thể được coi là một điều kiện bất thường và do đó xứng đáng với một ngoại lệ.

Giải pháp khác và giải pháp "chức năng" hơn là viết chức năng của bạn để nếu nó không ảnh hưởng đến một số ứng dụng, hãy viết nó để không có hiệu lực. Vì vậy, ví dụ, nếu bạn đã có một hàm tổng hợp, có nó thêm 0 trong các trường hợp bạn sẽ có "bị hỏng" từ.

+3

Việc ném ngoại lệ cho điều khiển luồng không phải là vấn đề về phong cách. Đó là một vấn đề của hương vị. Hương vị tồi tệ, đó là. –

+1

Nghe có vẻ như đó là vấn đề cuồng nhiệt mù hơn là nếm thử. Nó thực hiện công việc, đơn giản hơn, sạch hơn và thanh lịch hơn hầu hết các giải pháp khác. Tôi đồng ý với Charlie về việc này. Nếu bạn * cần * cần phải thoát khỏi for_each, thì 1) có thể có vấn đề thiết kế cơ bản và 2) ném ngoại lệ là giải pháp đơn giản nhất. Miễn là ngoại lệ có thể bị cô lập một cách rõ ràng (đó là một dòng mã bạn cần bọc trong try/catch), sử dụng nó để kiểm soát luồng là có thể chấp nhận được. – jalf

+0

Đó là loại giống như zealotry chống goto cũ. Có, gotos có thể có hại, nhưng đôi khi trong một số ngôn ngữ, chúng là lựa chọn tốt nhất có sẵn. –

6

Nếu bạn muốn thực hiện một số hành động trong khi điều kiện không hài lòng, có thể bạn cần thuật toán thay đổi trên một cái gì đó như std::find_if?

+2

Đây là cách dễ dàng. Chỉ cần trả lại đúng khi bạn muốn dừng lại. –

4

Như đã được hiển thị bởi những người khác, nó chỉ có thể đạt được với các giải pháp mà IMHO làm xáo trộn mã.

Vì vậy, đề xuất của tôi là thay đổi for_each thành vòng lặp thông thường. Điều này sẽ làm cho nó dễ thấy hơn với những người khác mà bạn đang sử dụng break (và thậm chí có thể tiếp tục).

20

Bạn có thể sử dụng tiêu chuẩn :: any_of (hoặc std :: all_of hoặc std :: none_of) ví dụ: như thế này:

std::vector<int> a; 
// ...  
std::all_of(a.begin(), a.end(), [&](int val) { 
    // return false if you want to break, true otherwise 
    }); 

Tuy nhiên, đây là một giải pháp lãng phí (giá trị trả lại không thực sự sử dụng cho bất cứ điều gì), và bạn nên viết bạn sở hữu vòng lặp.

+2

Câu trả lời hay nhất thực sự đã giúp – York

+0

Câu trả lời hay ... – Ash

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