2013-07-25 15 views

cevap

12

Örnek sayısını belirleyecek ve beklenen sayıma ulaşıldığında tamamlanacak durum bilgisi olan bir yüklem oluşturmanız gerekir. Şimdi sorun, algoritmanın değerlendirilmesi sırasında kaç kez kopyalamanın kopyalanacağı konusunda bir garanti olmamasıdır, bu yüzden bu durumu, kendiliğinden çok çirkin kılan, kendi kendini kurmanın dışında tutmalısınız. Bu sık sık geliyor ve performanstaki endişe değilseniz

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

, dahili olarak saymak bir shared_ptr yarattı ve onu güncellenen bir yüklem adaptörü yazabilirim. Aynı bağdaştırıcının birden çok kopyası aynı gerçek sayım nesnesini paylaşır.

Başka bir alternatif daha kolay olabilir find_nth_if uygulamak olabilir.

#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

Değer tarafından ele geçirilen bir “mutable” lambda da çalışır mı? – Yakk

+0

@Yakk: Hayır. Algoritmanın uygulanmasının, yüklemenin kopyalanmasına izin verilir, böylece lambda'nızın farklı örneklerinde güncellenen sayımla sonuçlanabilir, bu da algoritmanın yanlış olmasına yol açacaktır. -th, nerede 'M' N 'örneğini) –

+0

@ DavidRodríguez-dribeas +1 ama sizin yaklaşımınızı biraz daha kolay yapabilirsiniz ile boost :: filter_iterator', benim cevabıma bakın – TemplateRex

3

olduğu gibi David'in cevabı gayet iyi. bana sadece yüklem, Boost.Iterator kitaplığını kullanarak Yineleyicilerin içinde özetlenmiştir işaret edelim özellikle buna da çok daha fazla algoritmalar için kullanılabileceğini avantajı (sayma örneğin) sahiptir boost::filter_iterator adaptör,:

#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. Bu yazdırılacaktır 2 (2 eleman> 0, 1'den başlayan sayma find_if maçlar n==1 ile find_if_nth. Yüklem i > 10 olarak değiştirilir veya n'inci elemanı n = 6 için değiştirilirse, bu uç yineleyici geri dönecektir.

yüzden

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

Ve kesinlikle std::find_if kullanmak istiyorsanız, gibi bir şey olabilir:

+0

[tanımsız davranış] (http://coliru.stacked-crooked.com/view?id=02ff2c799552600fb174b17d9eacd458-25783dc5e0579471d3e326cae35dc982) - ['find_if_nth '] eşleşmesi için daha dikkatli ilerlemeniz gerekiyor (http: //coliru.stacked- crooked.com/view?id=2d3e9b6357b3445aa30b374d6a149847-25783dc5e0579471d3e326cae35dc982), hangi tür yığınlar.Element elementinin olmaması durumunda daha güvenli hale getirmenin daha temiz bir yolunu biliyor musunuz? Sınır koşulunu işaret etmek için – Yakk

+0

@Yakk tnx. Gerçekten düşünmemiştim, düzeltecek. – TemplateRex

+0

@Yakk Her zamanki 'find_if' gibi davranması gereken 'find_if_nth' yazdım ve nth eşleşme bulunmadığında son yineleyiciyi döndürdüm. Bu yüklemi 'for' döngüsünden kaldırıldığı için hala bu çözümü seviyorum. – TemplateRex

3

bir STL benzeri işlev şablonu olurdu

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, bir cevap henüz kullanıcı tarafından cevap olarak işaretlendi ... – Manu343726

+0

@ Manu343726: İyi bir cevap yazmak için çok fazla zaman harcadım gibi görünüyor:/Ayrıca, bence cevabım kabul edilen cevaptan üstün. .;) –

+0

Normalde cevap yazmak için çok zaman harcadım. Ve uzun, tam olarak açıklanmış bir cevabımı yazarken, iki satırlık cevaplar ortaya çıkıyor. Seni anlıyorum :) – Manu343726