2012-04-02 13 views
11

La notazione lambda ha reso gli algoritmi di stl più accessibili. Sto ancora imparando a decidere quando è utile e quando ricadere in un buon vecchio stile. Spesso, diventa necessario iterare su due (o più) contenitori della stessa dimensione, in modo tale che gli elementi corrispondenti siano correlati, ma per qualche motivo non sono raggruppati nella stessa classe.std :: for_each funziona su più di un intervallo di iteratori

Una funzione utilizza un ciclo for per ottenere che sarebbe simile a questa:

template<typename Data, typename Property> 
void foo(vector<Data>& data, vector<Property>& prop) { 
    auto i_data = begin(data); 
    auto i_prop = begin(prop); 
    for (; i_data != data.end(); ++i_data, ++i_prop) { 
     if (i_prop->SomePropertySatistfied()) { 
      i_data->DoSomething(); 
     } 
    } 
} 

Per poter utilizzare for_each, ho bisogno di una versione di esso che gestisce più intervalli; qualcosa di simile:

template<typename InputIter1, typename InputIter2, typename Function> 
Function for_each_on_two_ranges(InputIter1 first1, InputIter1 last1, InputIter2 first2, Function f) { 
    for (; first1 != last1; ++first1, ++first2) { 
     f(*first1, *first2); 
    } 
    return f; 
} 

Con questa versione, il codice di cui sopra sarebbe simile a questa:

template<typename Data, typename Property> 
void foo_two_ranges(vector<Data>& data, vector<Property>& prop) { 
    for_each_on_two_ranges(begin(data), end(data), begin(prop), [](Data& d, Property& p) { 
     if (p.SomePropertySatistfied()) { 
      d.DoSomething(); 
     } 
    }); 
} 

C'è un modo equivalente di ottenere lo stesso risultato utilizzando algoritmi STL?

EDIT

ho trovato la risposta esatta alla mia domanda in forma di boost :: for_each esecuzione su boost :: gamma. Ho aggiunto la risposta, con codice di esempio per motivi di completezza.

+1

Perché non usare semplicemente "for_each_two_ranges" che hai già scritto? – Puppy

+1

Sembra una cosa così comune per me, che penserei che sia stato risolto da qualcuno già – killogre

+6

Penso che "zip_iterator" di Boost.Iterator fa quello che vuoi. Vedi http://www.boost.org/doc/libs/1_49_0/libs/iterator/doc/zip_iterator.html per i dettagli. – celtschk

risposta

8

1) Gli algoritmi in STL non sono pensati per coprire ogni possibile caso, se è necessario for_each_on_two_ranges quindi scrivilo (come hai) e usalo. La bellezza dell'STL è così estensibile e l'hai estesa con un nuovo utile algo.

2) Se ciò non funziona, non è necessario utilizzare buoni for-loop di vecchio stile, è possibile utilizzare fantastici nuovi for-loops, invece!

Come un'altra risposta ha detto, boost::zip_iterator è tuo amico qui, ma non deve essere difficile da usare. Ecco una soluzione con un adattatore intervallo che viene implementata con zip_iterator

template<typename Data, typename Property> 
void foo(vector<Data>& data, vector<Property>& prop) { 
    for (auto i : redi::zip(data, prop)) 
     if (i.get<1>().SomePropertySatistfied()) 
      i.get<0>.DoSomething(); 
} 

Tale zip funzione crea un adattatore con begin() e end() membri che restituiscono un boost::zip_iterator, quindi la variabile loop è una tupla degli elementi di ogni contenitore sottostante (e dato che è un modello di variadic si può fare per qualsiasi numero di contenitori, in modo da non avere bisogno di scrivere for_each_for_three_ranges e for_each_for_four_ranges ecc)

si potrebbe anche usare con for_each

auto z = redi::zip(data, prop); 
typedef decltype(z)::iterator::reference reference; 

for_each(begin(z), end(z), [](reference i) { 
    if (i.get<1>().SomePropertySatistfied()) { 
     i.get<0>().DoSomething(); 
    } 
}); 
+0

Sembra molto bello. Mi piace il fatto che sia variadico e che il codice sia relativamente compatto. Vedo che ho bisogno di fare alcuni compiti sugli adattatori di gamma. Grazie! – killogre

+0

Redi è uno spazio dei nomi in boost? Quali sono le intestazioni richieste? Thankee. –

+0

@JiveDadson, no è il mio spazio dei nomi, ho collegato il lavoro [zip] (https://gitorious.org/redistd/redistd/blobs/master/include/redi/zip.h) al sorgente (e l'ho fatto di nuovo ora) –

1

ha un sovraccarico che opera su due sequenze in parallelo. Avresti bisogno di un iteratore di output nullo per assorbire i risultati, se non ti interessasse raccoglierne.

+0

for_each e transform hanno una semantica diversa [collegamento] (http://drdobbs.com/cpp/184403769). lo standard richiede che la trasformazione sia non mutante, ma non richiede quella di for_each. Ad ogni modo, il sovraccarico che menzioni funziona solo per gli iteratori forward identici – killogre

+0

@Killogre: 'std :: transform' consente la mutazione impostando l'iteratore di output su uno degli iteratori di input. Ma sì, non è davvero progettato per questa esatta situazione. –

5

Dopo aver letto su boost :: zip_iterator e boost :: iterator_range come suggerito da alcune risposte, mi sono imbattuto nel extension algorithms in boost::range, e ho trovato l'esatto parallelo dell'algoritmo che ho scritto per due gamme, ma con intervalli di boost.

un codice di lavoro per l'esempio sarebbe

#include <boost/range/algorithm_ext/for_each.hpp> 

template<typename Data, typename Property> 
void foo_two_ranges(vector<Data>& data, vector<Property>& prop) { 
    auto rng1 = boost::make_iterator_range(data.begin(), data.end()); 
    auto rng2 = boost::make_iterator_range(prop.begin(), prop.end()); 
    boost::for_each(rng1, rng2, [](Data& d, Property& p) { 
     if (p.SomePropertySatistfied()) { 
      d.DoSomething(); 
     } 
    }); 
} 

Alcuni involucri e funzioni di utilità, simili in mente di cosa @ Jonathan Wakely suggerito, può renderlo ancora più fruibile.

+0

I percorsi di inclusione dovrebbero sempre usare/non \ anche su Windoze, altrimenti "percorso \ nomi \ come \ questo.hpp" 'avrà caratteri di nuova riga e TAB. Inoltre li rende portatili a tutti i sistemi, non solo a Windoze. –

+0

Lo snippet ora è Unix-friendly, @JonathanWakely :) – killogre

+2

Nota che puoi scrivere 'boost :: for_each (data, prop, ...)' - non c'è bisogno di chiamare 'make_iterator_range'. – ecatmur

Problemi correlati