2013-07-25 97 views
9

Có một vài số std::algorithm/lambda function để truy cập phần tử nth thỏa mãn điều kiện nhất định không. Bởi vì std::find_if sẽ truy cập vào cái đầu tiên, vậy có tương đương để tìm số nth không?Tìm phần tử thứ n thỏa mãn điều kiện?

Trả lời

12

Bạn cần phải tạo một biến vị ngữ trạng thái sẽ đếm số lượng cá thể và sau đó hoàn thành khi đạt đến số lượng mong đợi. Bây giờ vấn đề là không có sự đảm bảo về số lần vị từ sẽ được sao chép trong quá trình đánh giá thuật toán, vì vậy bạn cần duy trì trạng thái đó bên ngoài chính biến vị ngữ, điều này làm cho nó hơi xấu xí, nhưng bạn có thể làm :

iterator which; 
{ // block to limit the scope of the otherwise unneeded count variable 
    int count = 0; 
    which = std::find_if(c.begin(), c.end(), [&count](T const & x) { 
     return (condition(x) && ++count == 6) 
    }); 
}; 

Nếu điều này xuất hiện thường xuyên và bạn không quan tâm đến hiệu suất, bạn có thể viết bộ điều hợp biến vị ngữ đã tạo shared_ptr cho số nội bộ và cập nhật. Nhiều bản sao của cùng một bộ điều hợp sẽ chia sẻ cùng một đối tượng đếm thực tế.

Một giải pháp thay thế khác là triển khai find_nth_if, điều này có thể đơn giản hơn.

#include <iterator> 
#include <algorithm> 

template<typename Iterator, typename Pred, typename Counter> 
Iterator find_if_nth(Iterator first, Iterator last, Pred closure, Counter n) { 
    typedef typename std::iterator_traits<Iterator>::reference Tref; 
    return std::find_if(first, last, [&](Tref x) { 
    return closure(x) && !(--n); 
    }); 
} 

http://ideone.com/EZLLdL

+0

Một lambda 'mutable' có thể thu được bằng giá trị có hoạt động tốt không? – Yakk

+0

@Yakk: Không. Việc thực hiện thuật toán được phép sao chép vị từ để bạn có thể kết thúc với số đếm được cập nhật trong các trường hợp khác nhau của lambda, sau đó sẽ dẫn đến kết quả của thuật toán sai (tìm M -, trong đó ví dụ 'M> N') –

+0

@ DavidRodríguez-dribeas +1 nhưng cách tiếp cận của bạn có thể được thực hiện dễ dàng hơn một chút với' boost :: filter_iterator', xem câu trả lời của tôi – TemplateRex

3

câu trả lời của David là tốt vì nó là. Hãy để tôi chỉ ra rằng biến vị ngữ có thể được trừu tượng hóa thành các trình vòng lặp bằng cách sử dụng thư viện Boost.Iterator, cụ thể là bộ điều hợp boost::filter_iterator, có lợi thế là nó có thể được sử dụng cho nhiều thuật toán hơn nữa (đếm ví dụ):

#include <iostream> 
#include <vector> 
#include <algorithm> 
#include <boost/iterator/filter_iterator.hpp> 

template<class ForwardIt, class Predicate, class Size> 
ForwardIt find_if_nth(ForwardIt first, ForwardIt last, Predicate pred, Size n) 
{ 
    auto vb = boost::make_filter_iterator(pred, first, last); 
    auto const ve = boost::make_filter_iterator(pred, last, last); 
    while (vb != ve && --n) 
     ++vb; 
    return vb.base(); 
} 

int main() 
{ 
    auto const v = std::vector<int>{ 0, 0, 3, 0, 2, 4, 5, 0, 7 }; 
    auto const n = 2; 
    auto const pred = [](int i){ return i > 0; }; 
    auto const nth_match = find_if_nth(v.begin(), v.end(), pred, n); 

    if (nth_match != v.end()) 
     std::cout << *nth_match << '\n'; 
    else 
     std::cout << "less than n elements in v matched predicate\n"; 
} 

Live example. Điều này sẽ in 2 (yếu tố thứ 2> 0, đếm bắt đầu từ 1, do đó find_if trận find_if_nth với n==1. Nếu vị được thay đổi để i > 10 hoặc nếu các yếu tố thứ n được thay đổi để n = 6, nó sẽ trả lại lặp kết thúc.

+0

[hành vi không xác định] (http://coliru.stacked-crooked.com/view?id=02ff2c799552600fb174b17d9eacd458-25783dc5e0579471d3e326cae35dc982) - bạn cần phải tiến hành cẩn thận hơn để khớp với ['find_if_nth'] (http: //coliru.stacked- crooked.com/view?id=2d3e9b6357b3445aa30b374d6a149847-25783dc5e0579471d3e326cae35dc982), loại này tăng lên.Bạn có biết một cách sạch hơn để làm cho nó an toàn hơn trong trường hợp phần tử thứ n không tồn tại? – Yakk

+0

@Yakk tnx để chỉ ra điều kiện biên. Đã không thực sự nghĩ về nó, sẽ sửa chữa nó. – TemplateRex

+0

@Yakk Tôi đã viết 'find_if_nth' sẽ hoạt động như' find_if' bình thường và trả về trình lặp kết thúc khi không tìm thấy kết quả phù hợp nào. Bởi vì biến vị ngữ đã được đưa ra khỏi vòng lặp 'for', tôi vẫn thích giải pháp này. – TemplateRex

3

Một STL-như hàm mẫu sẽ là:

template<class InputIterator, class NthOccurence class UnaryPredicate> 
InputIterator find_nth_if(InputIterator first, InputIterator last, NthOccurence Nth, UnaryPredicate pred) 
{ 
    if (Nth > 0) 
     while (first != last) { 
      if (pred(*first)) 
       if (!--Nth) 
        return first; 
      ++first; 
     } 
    return last; 
} 

Và nếu bạn hoàn toàn muốn sử dụng std::find_if, bạn có thể có một cái gì đó như:

template<class InputIterator, class NthOccurence class UnaryPredicate> 
InputIterator find_nth_if(InputIterator first, InputIterator last, NthOccurence Nth, UnaryPredicate pred) 
{ 
    if (Nth > 0) { 
     do 
      first = std::find_if(first, last, pred); 
     while (!--Nth && ++first != last); 
     return first; 
    } 
    else 
     return last; 
} 
+0

eehm, câu trả lời được đánh dấu là câu trả lời của người dùng ... – Manu343726

+0

@ Manu343726: Có vẻ như tôi đã dành quá nhiều thời gian để viết một câu trả lời hay:/Ngoài ra, tôi nghĩ câu trả lời của tôi _may be_ vượt trội so với câu trả lời được chấp nhận .. .;) –

+0

Tôi thường mất nhiều thời gian để viết câu trả lời. Và trong khi Im viết câu trả lời dài đầy đủ của tôi, hàng ngàn câu trả lời hai dòng xuất hiện. Tôi hiểu bạn :) – Manu343726