İçlerinde "if" koşulu olan "for" döngülerini C ++ ile nasıl önleyebilirim?


111

Neredeyse yazdığım tüm kodlarla, genellikle koleksiyonlarda, sonuçta içlerinde saf "eğer" koşullarıyla sonuçlanan set azaltma problemleriyle uğraşıyorum. İşte basit bir örnek:

for(int i=0; i<myCollection.size(); i++)
{
     if (myCollection[i] == SOMETHING)
     {
           DoStuff();
     }
}

İşlevsel dillerle, koleksiyonu başka bir koleksiyona indirgeyerek (kolayca) sorunu çözebilir ve ardından tüm işlemleri küçültülmüş setimde gerçekleştirebilirim. Sözde kodda:

newCollection <- myCollection where <x=true
map DoStuff newCollection

Ve C # gibi diğer C değişkenlerinde, bir where cümlesiyle azaltabilirim

foreach (var x in myCollection.Where(c=> c == SOMETHING)) 
{
   DoStuff();
}

Ya da daha iyisi (en azından gözlerime)

myCollection.Where(c=>c == Something).ToList().ForEach(d=> DoStuff(d));

Kuşkusuz, çok fazla paradigma karışımı ve öznel / fikir temelli stil yapıyorum, ancak yardım edemem ama bu tercih edilen tekniği C ++ ile kullanmama izin verebilecek gerçekten temel bir şeyi kaçırdığımı hissediyorum. Biri beni aydınlatabilir mi?


7
C ++ standart kitaplık işlevselliği dışında deneyebilirsiniz std::copy_if, ancak seçimler tembel değil
milleniumbug

14
Range-v3 ilginizi çekebilir . Ayrıca C ++ 'ya TS olarak gelmeli ve umarız gelecekteki bir sürümde standartlaştırılmalıdır.
NathanOliver

12
Bahsettiğiniz ifforkısmın sadece diğer örneklere işlevsel olarak eşdeğer olmadığını, aynı zamanda birçok durumda muhtemelen daha hızlı olacağını da belirtme gereğini hissediyorum . Ayrıca, işlevsel tarzı sevdiğini iddia eden biri için, teşvik ettiğiniz şey, DoStuffaçıkça yan etkileri olduğu için, işlevsel programlamanın çok sevilen saflık kavramına aykırı görünüyor .
Pharap

60
İnsanların tüm mantığı tek bir satırda birleştirmesinin neden bir şekilde daha iyi veya daha okunabilir görünmesini sağladığını gerçekten anlamadım . Sizin C ++ pasajı çok üstünde arayla senin olasılıklar tüm dışarı bana en okunabilir. Verimlilik değişmeyeceğinden, sildiğiniz kod satırı sayısı ile ödeme almadığınız sürece neden bunu yazmamayı tercih ettiğinizi anlayamıyorum.
Cody Grey

10
@CodyGray Kabul Edildi: Sadece sözdizimsel şeker. Ve soru başlığı yanıltıcıdır, çünkü dallanmadan kaçınmak ve onu soyutlama altında saklamak çok farklıdır .
edmz

Yanıtlar:


99

IMHO, içinde if ile bir for döngüsü kullanmak daha basit ve daha okunaklı. Ancak, bu sizin için can sıkıcıysa, for_each_ifaşağıdaki gibi bir tane kullanabilirsiniz :

template<typename Iter, typename Pred, typename Op> 
void for_each_if(Iter first, Iter last, Pred p, Op op) {
  while(first != last) {
    if (p(*first)) op(*first);
    ++first;
  }
}

usecase:

std::vector<int> v {10, 2, 10, 3};
for_each_if(v.begin(), v.end(), [](int i){ return i > 5; }, [](int &i){ ++i; });

Canlı Demo


10
Bu son derece zekice. Ayrıca bunun basit olmadığı konusunda hemfikir olacağım ve muhtemelen sadece başkaları tarafından tüketilen C ++ 'ı programlarken koşulları kullanacağım. Ama bu tam da kendi kişisel kullanımım için ihtiyacım olan şey! :)
Darkenor

14
@Default Kapsayıcılar yerine yineleyici çiftlerini geçirmek hem daha esnek hem de deyimsel C ++ 'dır.
Mark B

8
@Slava, genel olarak aralıklar algoritma sayısını azaltmaz. Örneğin, yine ihtiyaç find_ifve findonlar aralıkları veya Yineleyicilerin çiftleri üzerinde çalışmak ister. (Örneğin birkaç istisnalar vardır for_eachve for_each_n). Her hapşırma için yeni algos yazılmasını önlemek için bir yol yerine örneğin, mevcut algos ile farklı işlem kullanmaktır for_each_ifgeçirilen çağrılabilir içine gömmek durumda for_eachörneğinfor_each(first, last, [&](auto& x) { if (cond(x)) f(x); });
Jonathan Wakely

9
İlk cümleyle hemfikir olmak zorundayım: -if çözümü standardı çok daha okunabilir ve üzerinde çalışılması daha kolaydır. Bence lambda sözdizimi ve başka bir yerde tanımlanmış bir şablonun sadece basit bir döngüyü işlemek için kullanılmasının diğer geliştiricileri rahatsız edeceğini veya muhtemelen kafasını karıştıracağını düşünüyorum. Yerellik ve performanstan ödün veriyorsun ... ne için? Tek satırda bir şeyler yazabilmek?
user1354557

45
Öksürük @Darkenor, genellikle " son derece zeki" programlamadan kaçınılmalıdır çünkü gelecekteki benlik dahil herkesi kızdırır .
Ryan

48

Boost, w / aralık tabanlı olarak kullanılabilecek aralıklar sağlar. Aralıklar bunlar temel veri yapısı kopyalamazsanız bu, bunlar sadece bir 'bakış' avantajına sahiptir (olduğundan, begin(), end()aralığı için ve operator++(), operator==()yineleyici için). Bu ilginizi çekebilir: http://www.boost.org/libs/range/doc/html/range/reference/adaptors/reference/filtered.html

#include <boost/range/adaptor/filtered.hpp>
#include <iostream>
#include <vector>

struct is_even
{
    bool operator()( int x ) const { return x % 2 == 0; }
};

int main(int argc, const char* argv[])
{
    using namespace boost::adaptors;

    std::vector<int> myCollection{1,2,3,4,5,6,7,8,9};

    for( int i: myCollection | filtered( is_even() ) )
    {
        std::cout << i;
    }
}

1
Bunun yerine OPs örneğini kullanmayı önerebilir miyim, yani is_even=> condition, input=> myCollectionvb.
Varsayılan

Bu oldukça mükemmel bir cevap ve kesinlikle yapmak istediğim şey. Birisi bunu yapmak için tembel / ertelenmiş yürütme kullanan standart ve uyumlu bir yol bulmadıkça kabul etmekten vazgeçeceğim. Upvoted.
Darkenor

5
@Darkenor: Boost sizin için bir sorunsa (örneğin, şirket politikası ve yönetici bilgeliği nedeniyle bunu kullanmanız yasaklandıysa), filtered()sizin için basitleştirilmiş bir tanım bulabilirim - yani kullanmak daha iyidir bazı geçici kodlardan daha desteklenen bir kitaplık.
lorro

Sana tamamen katılıyorum. Bunu kabul ettim çünkü ilk gelen standart uyumlu yol çünkü soru destek kitaplığına değil, C ++ 'nın kendisine yönelikti. Ama bu gerçekten mükemmel. Ayrıca - evet, Boost'u absürt nedenlerle yasaklayan birçok yerde ne yazık ki çalıştım ...
Darkenor

@LeeClagett:? .
lorro

44

Kabul edilen yanıtın yaptığı gibi yeni bir algoritma oluşturmak yerine, koşulu uygulayan bir işlevle mevcut bir algoritmayı kullanabilirsiniz:

std::for_each(first, last, [](auto&& x){ if (cond(x)) { ... } });

Veya gerçekten yeni bir algoritma istiyorsanız for_each, yineleme mantığını kopyalamak yerine en azından orada yeniden kullanın:

template<typename Iter, typename Pred, typename Op> 
  void
  for_each_if(Iter first, Iter last, Pred p, Op op) {
    std::for_each(first, last, [&](auto& x) { if (p(x)) op(x); });
  }

Standart kitaplığı kullanmak için çok daha iyi ve daha net.
anonim

4
Çünkü std::for-each(first, last, [&](auto& x) {if (p(x)) op(x); });daha for (Iter x = first; x != last; x++) if (p(x)) op(x);}mı basit ?
user253751

2
@immibis'in standart kitaplığı yeniden kullanmasının, yineleyici geçerlilik denetimi gibi başka faydaları da vardır veya (C ++ 17'de), basitçe bir argüman ekleyerek paralel hale getirmenin çok daha kolay olması: std::for_each(std::execution::par, first, last, ...);Bunları el yazısı döngüye eklemek ne kadar kolay?
Jonathan Wakely

1
#pragma omp parallel for
Mark K Cowan

2
@mark üzgünüm, kaynak kodunuzun veya yapı zincirinizin rasgele bir tuhaflığı, sinir bozucu derecede kırılgan standart olmayan paralel derleyici uzantısının, herhangi bir teşhis olmaksızın sıfır performans artışı oluşturmasına neden oldu.
Yakk - Adam Nevraumont

21

Kaçınma fikri

for(...)
    if(...)

bir anti-model olarak yapılar çok geniştir.

Bir döngünün içinden belirli bir ifadeyle eşleşen birden çok öğeyi işlemek tamamen iyidir ve kod bundan daha net olamaz. İşlem ekrana sığmayacak kadar büyürse, bu bir alt yordam kullanmak için iyi bir nedendir, ancak yine de koşullu en iyi döngü içine yerleştirilir, yani

for(...)
    if(...)
        do_process(...);

büyük ölçüde tercih edilir

for(...)
    maybe_process(...);

Sadece bir eleman eşleştiğinde bir anti-model haline gelir, çünkü o zaman önce elemanı aramak ve işlemi döngünün dışında gerçekleştirmek daha net olacaktır.

for(int i = 0; i < size; ++i)
    if(i == 5)

bunun aşırı ve açık bir örneğidir. Daha incelikli ve dolayısıyla daha yaygın olan bir fabrika modelidir.

for(creator &c : creators)
    if(c.name == requested_name)
    {
        unique_ptr<object> obj = c.create_object();
        obj.owner = this;
        return std::move(obj);
    }

Bunu okumak zordur, çünkü gövde kodunun yalnızca bir kez çalıştırılacağı açık değildir. Bu durumda, aramayı ayırmak daha iyi olacaktır:

creator &lookup(string const &requested_name)
{
    for(creator &c : creators)
        if(c.name == requested_name)
            return c;
}

creator &c = lookup(requested_name);
unique_ptr obj = c.create_object();

Hala bir ifvardır for, ancak bağlamdan ne yaptığı netleşir, arama değişmedikçe (örneğin a'ya) bu kodu değiştirmeye gerek yoktur mapve bunun create_object()yalnızca bir kez çağrıldığı hemen açıktır , çünkü bir döngü içinde değil.


Düşünceli ve dengeli bir genel bakış olarak bunu seviyorum, bir anlamda sorulan soruya cevap vermeyi reddetse bile. for( range ){ if( condition ){ action } }-Stili, her seferinde bir yığın şeyleri okumayı kolaylaştırdığını ve sadece temel dil yapılarının bilgisini kullandığını görüyorum .
PJTraill

@PJTraill, sorunun ifade ediliş biçimi bana Raymond Chen'in kargoyla kültüre alınan ve bir şekilde mutlak hale gelen for-if antipattern'e karşı rantını hatırlattı. Bunun for(...) if(...) { ... }çoğu zaman en iyi seçim olduğuna tamamen katılıyorum (bu nedenle, eylemi bir alt yordama bölme önerisini nitelendirdim).
Simon Richter

1
Bana her şeyi açıklığa kavuşturan bağlantı için teşekkürler: " if-if " adı yanıltıcıdır ve " hepsi için-eğer " veya " aramadan kaçınma " gibi bir şey olmalıdır . Bana 2005'te Vikipedi tarafından Soyutlama tersine çevrilmesininkarmaşık yapının üzerinde basit yapılar oluşturduğu ” olarak tanımlanma şeklini hatırlatıyor . (birlerin) - ta ki ben onu yeniden yazana kadar! Aslında for(…)if(…)…aramanın gerçekleştiği tek yer olsaydı , arama-işlem-çıkış biçimini düzeltmek için acele etmem bile .
PJTraill

17

İşte hızlı nispeten minimum filter işlev.

Bir yüklem alır. Yinelenebilir alan bir işlev nesnesi döndürür.

Bir for(:)döngüde kullanılabilen bir yinelenebilir döndürür .

template<class It>
struct range_t {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  bool empty() const { return begin()==end(); }
};
template<class It>
range_t<It> range( It b, It e ) { return {std::move(b), std::move(e)}; }

template<class It, class F>
struct filter_helper:range_t<It> {
  F f;
  void advance() {
    while(true) {
      (range_t<It>&)*this = range( std::next(this->begin()), this->end() );
      if (this->empty())
        return;
      if (f(*this->begin()))
        return;
    }
  }
  filter_helper(range_t<It> r, F fin):
    range_t<It>(r), f(std::move(fin))
  {
      while(true)
      {
          if (this->empty()) return;
          if (f(*this->begin())) return;
          (range_t<It>&)*this = range( std::next(this->begin()), this->end() );
      }
  }
};

template<class It, class F>
struct filter_psuedo_iterator {
  using iterator_category=std::input_iterator_tag;
  filter_helper<It, F>* helper = nullptr;
  bool m_is_end = true;
  bool is_end() const {
    return m_is_end || !helper || helper->empty();
  }

  void operator++() {
    helper->advance();
  }
  typename std::iterator_traits<It>::reference
  operator*() const {
    return *(helper->begin());
  }
  It base() const {
      if (!helper) return {};
      if (is_end()) return helper->end();
      return helper->begin();
  }
  friend bool operator==(filter_psuedo_iterator const& lhs, filter_psuedo_iterator const& rhs) {
    if (lhs.is_end() && rhs.is_end()) return true;
    if (lhs.is_end() || rhs.is_end()) return false;
    return lhs.helper->begin() == rhs.helper->begin();
  }
  friend bool operator!=(filter_psuedo_iterator const& lhs, filter_psuedo_iterator const& rhs) {
    return !(lhs==rhs);
  }
};
template<class It, class F>
struct filter_range:
  private filter_helper<It, F>,
  range_t<filter_psuedo_iterator<It, F>>
{
  using helper=filter_helper<It, F>;
  using range=range_t<filter_psuedo_iterator<It, F>>;

  using range::begin; using range::end; using range::empty;

  filter_range( range_t<It> r, F f ):
    helper{{r}, std::forward<F>(f)},
    range{ {this, false}, {this, true} }
  {}
};

template<class F>
auto filter( F&& f ) {
    return [f=std::forward<F>(f)](auto&& r)
    {
        using std::begin; using std::end;
        using iterator = decltype(begin(r));
        return filter_range<iterator, std::decay_t<decltype(f)>>{
            range(begin(r), end(r)), f
        };
    };
};

Kestirmeler yaptım. Gerçek bir kütüphane, for(:)benim yaptığım niteleyici sözde fascades değil, gerçek yineleyiciler yapmalıdır.

Kullanım noktasında şuna benzer:

int main()
{
  std::vector<int> test = {1,2,3,4,5};
  for( auto i: filter([](auto x){return x%2;})( test ) )
    std::cout << i << '\n';
}

bu oldukça güzel ve baskılar

1
3
5

Canlı örnek .

Bu tür şeyleri ve daha fazlasını yapan Rangesv3 adlı C ++ 'ya önerilen bir ekleme var. boostayrıca filtre aralıkları / yineleyiciler de mevcuttur. boost ayrıca yukarıdakileri yazmayı çok daha kısa hale getiren yardımcılara da sahiptir.


15

Bahsetmek için yeterince alışan, ancak henüz bahsedilmeyen bir stil:

for(int i=0; i<myCollection.size(); i++) {
  if (myCollection[i] != SOMETHING)
    continue;

  DoStuff();
}

Avantajları:

  • DoStuff();Koşul karmaşıklığı arttığında girinti seviyesini değiştirmez . Mantıksal DoStuff();olarak, en üst düzeyde olmalıdırfor döngünün ve öyle.
  • Hemen o temizlemek yapar üzerinde döngü yinelenir SOMETHINGtoplama s, kapanış sonrasında hiçbir şey olmadığını doğrulamak için okuyucu gerektirmeden }ait ifbloğu.
  • Herhangi bir kitaplık veya yardımcı makro veya işlev gerektirmez.

Dezavantajları:

  • continue, diğer akış kontrol ifadeleri gibi, takip edilmesi zor koda yol açan şekillerde kötüye kullanılır ki, bazı insanlar bunların herhangi bir şekilde kullanılmasına karşı çıkarlar : Bazılarının takip ettiği continue, breakdiğerlerinden kaçınan geçerli bir kodlama stili vardır. bir de switch, bu engeller returnbir işlevi sonunda başka.

3
forBirçok satıra giden bir döngüde, iki satırlık bir "değilse, devam et" in çok daha net, mantıklı ve okunabilir olduğunu iddia ediyorum . İfade foriyi okuduktan hemen sonra "bunu atla" deyin ve sizin de söylediğiniz gibi döngünün kalan işlevsel yönlerini girintili yapmaz. Eğer continuedaha aşağı olduğunu ancak bazı netlik (bazı operasyon her zaman önce yapılacaktır yani eğer kurban ifaçıklamada).
anonim

11
for(auto const &x: myCollection) if(x == something) doStuff();

forBana C ++ 'ya özgü bir anlayışa benziyor . Sana?


Auto anahtar kelimesinin c ++ 11'den önce mevcut olduğunu düşünmüyorum, bu yüzden çok klasik bir c ++ olduğunu söylemem. Burada yorumda bir soru sorabilirsem, "auto const" derleyiciye tüm öğeleri istediği gibi yeniden düzenleyebileceğini söyler mi? Belki de derleyicinin dallanmadan kaçınmayı planlaması bu durumda daha kolay olacaktır.
mathreadler

1
@mathreadler İnsanlar "klasik c ++" hakkında endişelenmeyi ne kadar erken bırakırsa, o kadar iyidir. C ++ 11, dil için makroevrimsel bir olaydı ve 5 yaşında: çabaladığımız minimum değer olmalı . Her neyse, OP bunu ve C ++ 14'ü (daha da iyi!) Etiketledi. Hayır, auto constyineleme sırasına herhangi bir etkisi yoktur. Eğer tabanlı değişmekteydi yukarı bakacak olursak for, bunun temelde standart bir döngü yapar göreceksiniz begin()için end()örtülü dereferencing ile. Tekrarlanan konteynerin sipariş garantilerini (varsa) bozmasının hiçbir yolu yoktur; o yeryüzünden güldüm oldum olacak
underscore_d

1
@mathreadler, aslında öyleydi, sadece oldukça farklı bir anlamı vardı. Mevcut olmayan şey, menzil için ... ve diğer herhangi bir farklı C ++ 11 özelliğidir. Burada kastettiğim, range-fors, std::futures, std::functions, hatta bu anonim kapatmalar bile sözdiziminde çok iyi C ++ ish; her dilin kendi tabiri vardır ve yeni özellikleri birleştirdiğinde, onları eski iyi bilinen sözdizimini taklit etmeye çalışır.
bipll

@underscore_d, bir derleyicinin sanki kuralına uyulması koşuluyla herhangi bir dönüşüm gerçekleştirmesine izin verilir, değil mi?
bipll

1
Hmmm ve bununla ne kastedilebilir?
bipll

7

DoStuff (), gelecekte bir şekilde i'ye bağımlı olacaksa, bu garantili dalsız bit maskeleme varyantını öneririm.

unsigned int times = 0;
const int kSize = sizeof(unsigned int)*8;
for(int i = 0; i < myCollection.size()/kSize; i++){
  unsigned int mask = 0;
  for (int j = 0; j<kSize; j++){
    mask |= (myCollection[i*kSize+j]==SOMETHING) << j;
  }
  times+=popcount(mask);
}

for(int i=0;i<times;i++)
   DoStuff();

Popcount, popülasyon sayımı yapan herhangi bir işlevdir (bit sayısı = 1). İ ve komşuları ile daha gelişmiş kısıtlamalar koyma özgürlüğü olacak. Buna gerek yoksa, iç halkayı soyabilir ve dış halkayı yeniden yapabiliriz.

for(int i = 0; i < myCollection.size(); i++)
  times += (myCollection[i]==SOMETHING);

ardından bir

for(int i=0;i<times;i++)
   DoStuff();

6

Ayrıca, koleksiyonu yeniden sıralamayı önemsemiyorsanız, std :: partition ucuzdur.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

void DoStuff(int i)
{
    std::cout << i << '\n';
}

int main()
{
    using namespace std::placeholders;

    std::vector<int> v {1, 2, 5, 0, 9, 5, 5};
    const int SOMETHING = 5;

    std::for_each(v.begin(),
                  std::partition(v.begin(), v.end(),
                                 std::bind(std::equal_to<int> {}, _1, SOMETHING)), // some condition
                  DoStuff); // action
}

Ancak std::partitionkabı yeniden sipariş eder.
celtschk

5

Yukarıdaki çözümlerin karmaşıklığına hayran kaldım. Basit bir öneride #define foreach(a,b,c,d) for(a; b; c)if(d)bulunacaktım, ancak birkaç açık eksikliği var, örneğin, döngünüzde noktalı virgül yerine virgül kullanmayı unutmamalısınız ve aveya içinde virgül operatörünü kullanamazsınız c.

#include <list>
#include <iostream>

using namespace std; 

#define foreach(a,b,c,d) for(a; b; c)if(d)

int main(){
  list<int> a;

  for(int i=0; i<10; i++)
    a.push_back(i);

  for(auto i=a.begin(); i!=a.end(); i++)
    if((*i)&1)
      cout << *i << ' ';
  cout << endl;

  foreach(auto i=a.begin(), i!=a.end(), i++, (*i)&1)
    cout << *i << ' ';
  cout << endl;

  return 0;
}

3
Bazı yanıtların karmaşıklığı yalnızca yüksektir çünkü bunlar önce yeniden kullanılabilir genel bir yöntem gösterir (bunu yalnızca bir kez yaparsınız) ve sonra onu kullanır. Tüm uygulamanızda if koşuluna sahip bir döngünüz varsa etkili değildir, ancak binlerce kez olursa çok etkilidir.
gnasher729

1
Çoğu öneri gibi, bu da aralığı ve seçim koşulunu tanımlamayı zorlaştırır, kolaylaştırmaz. Ve bir makro kullanımı, burada sürprizler olmasa bile ifadelerin ne zaman (ve ne sıklıkla) değerlendirileceği konusundaki belirsizliği artırır.
PJTraill

2

İ: lerin önemli olması durumunda başka bir çözüm. Bu, doStuff () 'ı çağırmak için dizinleri dolduran bir liste oluşturur. Bir kez daha ana nokta, dallanmadan kaçınmak ve bunu boru hattına bağlanabilir aritmetik maliyetlerle ticaret yapmaktır.

int buffer[someSafeSize];
int cnt = 0; // counter to keep track where we are in list.
for( int i = 0; i < container.size(); i++ ){
   int lDecision = (container[i] == SOMETHING);
   buffer[cnt] = lDecision*i + (1-lDecision)*buffer[cnt];
   cnt += lDecision;
}

for( int i=0; i<cnt; i++ )
   doStuff(buffer[i]); // now we could pass the index or a pointer as an argument.

"Büyülü" çizgi, değeri korumak ve pozisyonda kalmak veya pozisyonu saymak ve değer eklemek için aritmetik olarak hesaplayan tampon yükleme çizgisidir. Bu yüzden, bazı mantık ve aritmetik ve belki de bazı önbellek isabetleri için potansiyel bir dalı değiştiriyoruz. Bunun yararlı olacağı tipik bir senaryo, doStuff () 'ın az miktarda ardışık düzenlenebilir hesaplama yapması ve çağrılar arasındaki herhangi bir dalın bu ardışık düzeni kesebilmesidir.

Sonra tampon üzerinde döngü yapın ve cnt'ye ulaşana kadar doStuff () çalıştırın. Bu sefer arabellekte depolanan mevcut i'ye sahip olacağız, böylece gerekirse doStuff () çağrısında kullanabiliriz.


1

Kod modelinizi, bir aralığın bir alt kümesine bir işlev uygulamak veya başka bir deyişle, tüm aralığa bir filtre uygulama sonucuna uygulamak olarak tanımlanabilir.

Bu, Eric Neibler'in serileri-v3 kitaplığı ile en basit şekilde elde edilebilir ; biraz göze batan bir şey olsa da, endekslerle çalışmak istiyorsun:

using namespace ranges;
auto mycollection_has_something = 
    [&](std::size_t i) { return myCollection[i] == SOMETHING };
auto filtered_view = 
    views::iota(std::size_t{0}, myCollection.size()) | 
    views::filter(mycollection_has_something);
for (auto i : filtered_view) { DoStuff(); }

Ancak endekslerden vazgeçmeye istekliysen, şunları elde edersin:

auto is_something = [&SOMETHING](const decltype(SOMETHING)& x) { return x == SOMETHING };
auto filtered_collection = myCollection | views::filter(is_something);
for (const auto& x : filtered_collection) { DoStuff(); }

hangi IMHO daha güzel.

Not - Aralık kitaplığı çoğunlukla C ++ 20'de C ++ standardına giriyor.


0

Sadece Mike Acton'dan bahsedeceğim, kesinlikle şöyle derdi:

Bunu yapmak zorunda kalırsanız, verilerinizle ilgili bir sorununuz var. Verilerinizi sıralayın!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.