Frage

Dies ist ein Follow-up auf meine Frage von gestern . Ich habe Scott Meyers' Warnung über write-only-Code auf meinem Kopf. Ich mag die Idee im Prinzip von Standardalgorithmen mit den Tasten oder Werte einer std :: map zuzugreifen, aber die Syntax erforderlich ist ein wenig barock IMHO. Lassen Sie uns sagen, dass ich alle Schlüssel der Karte, um einen Vektor entleeren wollen. Gegeben folgende Erklärungen,

typedef std::map<int, int> MyMap;
MyMap m;
std::vector<int> v;

, welcher Code besser verwaltbar ist (das heißt, potenziell weniger verwirrend)?

Option # 1:

std::transform(m.begin(),
               m.end(),
               std::back_inserter(v),
               std::tr1::bind(&MyMap::value_type::first, _1));

Option # 2:

for (MyMap::iterator i = m.begin(); i != m.end(); ++i)
{
    v.push_back(i->first);
}

Option 1 ist mehr Standard-Bibliothek-ish, aber ich muss geistig zersetzen, es zu verstehen, was los ist. Option 2 scheint leichter auf Kosten einer möglichen kleinen Laufzeitstrafe zu lesen. Ich bin verletzt nicht für CPU-Zeit, damit ich in Richtung Option lehnt bin 2. Teilt euch damit einverstanden? Gibt es eine dritte Option, die ich beachten sollte?

P. S. Im Laufe dieser Frage des Schreibens ich, dass die beste Art und Weise geschlossen (für mein Projekt) die Schlüssel einer std :: map zu lesen ist, sie in einem Seitenbehälter speichern aus und über das laufen. Die Wartbarkeit Frage steht noch though.

War es hilfreich?

Lösung

Clarity schlägt immer klug. Tun Sie, was Sie können später lesen.

Du bist nicht allein in der Annahme, dass der Standard-Code ein wenig stumpf ist. Der nächste C ++ Standard wird Lambda-Funktionen so können Sie mehr lesbar Code schreiben mit den Standardalgorithmen.

Andere Tipps

Die erste ist genauso lesbar und wartbar als das zweite - , wenn Sie wissen, was bind tut. Ich habe mit Boost-Arbeits :: Bind (im Wesentlichen identisch mit std::tr1::bind) lang genug, dass ich keine Probleme damit haben.

Sobald TR1 Teil der offiziellen Norm wird, können Sie davon ausgehen, dass jede zuständige C ++ Programmierer es verstehen. Bis dahin könnte es einige Schwierigkeiten aufwerfen, aber ich denke immer an die langfristige über die kurzfristigen.

Sie haben vergessen using namespace std::tr1::placeholders: P

Um ehrlich zu, für einfache Algorithmen so zu sein, ist dieser Code wahrscheinlich leichter zu pflegen. Aber ich neige dazu, tatsächlich für die ehemaligen (vor allem, wenn C ++ 1x gibt uns lambdas!), Weil es einen funktionalen Programmierstil betont, die ich persönlich mit dem Imperativ Stil bevorzuge eine Schleife zu verwenden.

Es ist wirklich eine unterschiedliche Hübe Sache; Standardalgorithmen sind besonders nützlich, wenn sie entweder komplex oder generisch, und das ist auch nicht.

Hier ist, wie es mit Lambda-Ausdrücke aussehen:

std::transform(m.begin(), m.end(), std::back_insterter(v),
               [](MyMap::value_type pair){ return pair.first; }
              );

Und tatsächlich, es gibt einen anderen Ansatz, die ich bevorzugen würde, aber für seine Ausführlichkeit:

using std::tr1::bind;
using std::tr1::placeholders::_1;
std::for_each(m.begin(), m.end(),
              bind(&std::vector<int>::push_back, v,
                   bind(&MyMap::value_type::first, _1)
                  )
             );

Und mit Lambda-Ausdrücke (dies ist wahrscheinlich im Großen und Ganzen die sauberste und am deutlichsten von allen Optionen):

std::for_each(m.begin(), m.end(),
              [&v](MyMap::value_type pair){v.push_back(pair.first);}
             );

Ich sage gehen für 2)

Leistung zu verbessern, können Sie m.end() aus der Schleife erhalten und den Raum in dem Vektor reservieren.

Kann nicht für C ++ 0x warten und die bereichsbasierte for-Schleife; das würde Ihre Schleife noch besser machen.

Gehen Sie

mit der Option # 1, siehe Scott Meyers, Effective STL Artikel # 43, Seite 181.

Wenn ich Ihre Frage sehe gestern war es nicht die bind (was ich viel verwenden), die ich gezwungen, zweimal hinschauen, um den Code zu verstehen, aber die Karte :: value_type :: ersten, die ich Gelegenheit gehabt zu sehr oft. Während ich darüber einig, dass „Klarheit immer klug schlägt“, ist Vertrautheit vor Klarheit erforderlich, und Sie gehen nicht mit Stilen vertraut machen Sie nicht verwenden ...

ich auch würde sagen, dass, während die Option 2 den beabsichtigten Zweck in Bezug auf das Verständnis klarer ist, ist es leichter einen Fehler verbergen würde (ein Fehler in Option 1 ist wahrscheinlicher, bei der Kompilierung sichtbar zu sein).

ich gehen würde, für Option # 3:

#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>

boost::push_back(v, m | boost::adaptors::map_keys);

Dies hat die Vorteile, dass es:

  1. kürzer

  2. verwendet eine benannte Funktion die Schlüssel zu bekommen

  3. ist (potentiell) effiziente (weil boost::push_back kann reserve() auf v nennen)

  4. und erfordert nicht den redundanten v.begin(), v.end() Paar.

Eine andere Art und Weise ist reiner Wahnsinn.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top