2009-04-20 5 views
2

Ich habe Code, der im Wesentlichen wie folgt aussieht:Wie benutzt man BOOST_FOREACH mit zwei std :: maps?

std::map<int, int> map1, map2; 
BOOST_FOREACH(int i, map1) 
{ 
    // do steps 1-5 here... 
} 
BOOST_FOREACH(int i, map2) 
{ 
    // do steps 1-5 (identical to above) here... 
} 

Gibt es eine Möglichkeit, die Karten zu verketten den doppelten Code in der zweiten Schleife zu beseitigen? Oder eine Möglichkeit, BOOST_FOREACH so zu erweitern, dass es in einem Durchgang über zwei verschiedene Karten iteriert? Offensichtlich möchte ich nicht die zeitliche Komplexität des Programms erhöhen (sonst könnte ich einfach eine neue Map erstellen und map1 und map2 einfügen). Ich habe das Gefühl, dass ich etwas rudimentäres hier vermisse.

+0

war es absichtlich, dass Sie über int iterieren? Sie sollten das Paar nicht nur int verwenden. oder hat Boost kürzlich die Möglichkeit erhalten, nur über den Wert zu iterieren? –

+0

Wenn überhaupt, würde ich erwarten, dass eine solche Funktion nur über den Schlüssel iteriert ... aber es funktioniert auch nicht mit meiner Version von Boost. – ephemient

Antwort

9

Sie eine Funktion definieren könnte:

typedef std::map<int, int> IntMap; 

void doStuffWithInt(IntMap::value_type &i) 
{ 
    // steps 1 to 5 
} 

BOOST_FOREACH(IntMap::value_type &i, map1) 
    doStuffWithInt(i); 
BOOST_FOREACH(IntMap::value_type &i, map2) 
    doStuffWithInt(i); 

Obwohl in diesem Fall könnte es sogar einfacher sein std::for_each zu verwenden:

for_each(map1.begin(), map1.end(), doStuffWithInt); 
for_each(map2.begin(), map2.end(), doStuffWithInt); 
+0

Wenn Sie Ints auf Ints abbilden (std :: map ), glaube ich, dass die Iteratoren auf std :: pair und nicht plain int zeigen werden? –

+0

ja wahrscheinlich, ich hatte nicht wirklich bemerkt, dass - um das Problem nicht zu verwirren, sollte es nicht zu schwierig sein, –

+0

+1 zu reparieren, manchmal sind die einfachsten Lösungen die besten :) –

2

Neben 1800-Lösung, die ich empfehlen würde, gibt es auch verschiedene Hacky-Lösungen:

for (int stage = 0; stage < 2; stage++) { 
    BOOST_FOREACH(int i, stage == 0 ? map1 : map2) { 
     ... 
    } 
} 

typedef std::map<int, int> intmap; 
std::vector<intmap *> v; 
v.push_back(&map1); 
v.push_back(&map2); 
BOOST_FOREACH(intmap *m, v) { 
    BOOST_FOREACH(int i, *m) { 
     ... 
    } 
} 

Hinweis: wenn ich sehe Kollegen schreiben Code wie dieser, manchmal überwältigt mich der unwiderstehliche Drang, sie zu erwürgen. Benutzung auf eigene Gefahr.

+0

+1 auf den Hinweis: Sie muss immer darüber nachdenken, was du bekommst und was du verlierst. Wenn Sie nur eine Komprimierung des Codes erhalten und die Lesbarkeit verlieren, wird dies nicht kompensiert. –

+0

Ich habe auch gewählt, weil ich beide deine Möglichkeiten mag, es zu tun. der erste ist ziemlich kompakt und lesbar. der zweite auch.obwohl ich es ändern würde, um intmap zu lesen * v [] = {& map1, & map2}; BOOST_FOREACH (intmap * m, v) {...}. Ich denke, "rohe" Arrays sind in diesem Fall in Ordnung. –

3

Die Idee hier ist, einen speziellen Typ von Iteratoren zu schreiben, um zwei Container virtuell zusammenzufassen, was BOOST_FOREACH betrifft. Beachten Sie, dass ich keinen neuen Container aus den beiden bestehenden Container erstelle. Ich spring einfach vom Ende des ersten Containers() zu dem Iterator begin() des zweiten Containers. Ich habe nicht versucht, die eigentliche merged_iterator Klasse zu schreiben, aber obwohl es ein bisschen lang zu schreiben ist, ist es technisch nicht schwierig. Ich bin wirklich überrascht, so etwas mit Google nicht gefunden zu haben. Ich habe jedoch nicht lange gesucht!

template<typename Container> 
boost::iterator_range< 
    merged_iterator<Container::iterator> 
    > 
concat_containers(Container& c1, Container& c2) 
{ 
    typedef merged_iterator<typename Container::iterator> MergedIterator; 
    typedef boost::iterator_range<MergedIterator> IteratorRange; 
    return IteratorRange(
    MergeIterator(c1.begin(), c1.end(), c2.begin(), c2.end()), 
    MergeIterator(c2.end(), c1.end(), c2.begin(), c2.end())); 
} 

// Now use a bit of magic to define merged_iterator<...> 
// And you'll be able to write 

BOOST_FOREACH(std::pair<int, int> i, concat_containers(map1, map2)) 
{ 
// Do whatever you want here 
} 
+0

Ich habe es vor ein paar Tagen für eine andere Frage implementiert. Beachten Sie, dass dies nicht bewiesen ist und möglicherweise einige Optimierungen erfordern: http://stackoverflow.com/questions/757153/concatenating-c-iterator-ranges-into-a-const-vector-member-variable-at-construc/757328# 757328 –

+0

+1 für einen sehr allgemeinen Ansatz, der z könnte sogar über verschiedene Containertypen hinweg funktionieren. Aber ich denke, dass dies für das vorliegende Problem übertrieben ist - machen Sie einfach eine Funktion wie 0800 INFORMATION vorgeschlagen! :) –

+0

Nur ist es für diese spezielle Frage übertrieben. Ich dachte nur, ich könnte versuchen und Spaß haben :) –

0

Von der Spitze von meinem Kopf, würde ich

std::map<int, int> map1, map2; 
std::map<int, int>& maps = { map1, map2 } 
BOOST_FOREACH(std::map<int, int> map, maps) 
    BOOST_FOREACH(int i, map) 
    { 
     // do steps 1-5 here... 
    } 
0

versuchen es uns erklärt here.

Sie können dies tun:

std::map<int,int> m; 
typedef std::pair<int,int> pair_t; 
BOOST_FOREACH(pair_t p, m) 
1

Der einfachste Weg ist wie folgt aus:

std::map<int, int> map1, map2; 
int key, value; 
BOOST_FOREACH(boost::tie(key, value), boost::join(map1, map2)) 
{ 
    // do steps 1-5 here... 
} 

Und keine Sorge diese Kommas nicht den Präprozessor verwirren wegen der Klammer.