2013-07-25 27 views

Odpowiedz

12

Należy utworzyć predykat stanowy, który policzy liczbę wystąpień, a następnie zakończy się po osiągnięciu oczekiwanej liczby. Problem polega na tym, że nie ma gwarancji, ile razy predykat zostanie skopiowany podczas oceny algorytmu, więc musisz zachować ten stan poza samym predykatem, co czyni go nieco brzydkim, ale możesz zrobić :

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) 
    }); 
}; 

Jeśli pojawia się często i nie są zaniepokojeni wydajność, można napisać adapter źródłowe, które stworzył shared_ptr do liczenia wewnętrznie i zaktualizowany. Wiele kopii tego samego adaptera udostępnia ten sam rzeczywisty obiekt zliczania.

Inną alternatywą byłoby wdrożenie find_nth_if, co może być prostsze.

#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

Czy "zmienna" lambda, która uchwycona przez wartość działa równie dobrze? – Yakk

+0

@Yakk: Nie. Wdrożenie algorytmu pozwala skopiować predykat, dzięki czemu możesz otrzymać zaktualizowaną liczbę w różnych instancjach twojego lambda, co spowoduje, że algorytm będzie nieprawidłowy (znajdując M -ty, gdzie instancja 'M> N') –

+0

@ DavidRodríguez-dribeas +1 ale twoje podejście może być wykonane nieco łatwiejsze z' boost :: filter_iterator', zobacz moją odpowiedź – TemplateRex

3

odpowiedź Dawida jest w porządku, jak to jest. Pozwolę sobie tylko zauważyć, że predykat może zostać wyodrębniony do iteratorów przy użyciu biblioteki Boost.Iterator, w szczególności adaptera boost::filter_iterator, który ma tę zaletę, że może być użyty do wielu innych algorytmów (np. Liczenia):

#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. Spowoduje to wydrukowanie 2 (2. element> 0, licząc począwszy od 1 tak, że find_if mecze find_if_nth z n==1. Jeżeli orzeczenie zostanie zmieniony na i > 10 lub jeśli n-ty element jest zmieniany na n = 6, zwróci iteracyjnej końca.

+0

[Niezdefiniowane zachowanie] (http://coliru.stacked-crooked.com/view?id=02ff2c799552600fb174b17d9eacd458-25783dc5e0579471d3e326cae35dc982) - musisz ostrożniej postępować, aby dopasować ['find_if_nth'] (http: //coliru.stacked- crooked.com/view?id=2d3e9b6357b3445aa30b374d6a149847-25783dc5e0579471d3e326cae35dc982), które to zwiększa.Czy znasz dokładniejszy sposób, aby uczynić go bezpieczniejszym w przypadku, gdy n-ty element nie istnieje? – Yakk

+0

@Tak tnx do wskazania warunku brzegowego. Nie myślałem o tym, naprawię to. – TemplateRex

+0

@Yakk Napisałem 'find_if_nth', który powinien zachowywać się jak zwykły' find_if' i zwracać iterator końca, gdy nie zostanie znaleziony żaden nth match. Ponieważ predykat został wyciągnięty z pętli 'for', nadal lubię to rozwiązanie. – TemplateRex

3

szablon funkcja STL-jak byłoby:

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; 
} 

A jeśli koniecznie chcesz używać std::find_if, można mieć coś takiego:

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, odpowiedź jest oznaczona jako odpowiedź użytkownika jeszcze ... – Manu343726

+0

@ Manu343726: Wydaje się, że zajęło mi zbyt dużo czasu, aby napisać dobrą odpowiedź:/Poza tym, myślę, że moja odpowiedź może być lepsza od zaakceptowanej odpowiedzi .. ;) –

+0

Zwykle też dużo czasu zajęło mi pisanie odpowiedzi. I chociaż piszę swoją długą, wyczerpującą odpowiedź, pojawiają się tysiące odpowiedzi w dwóch linijkach. Rozumiem cię :) – Manu343726