Welche der folgenden Aussagen finden Sie besser lesbar? Die handgeschriebene Schleife:
for (std::vector<Foo>::const_iterator it = vec.begin(); it != vec.end(); ++it)
{
bar.process(*it);
}
Oder der Algorithmusaufruf:
#include <algorithm>
#include <functional>
std::for_each(vec.begin(), vec.end(),
std::bind1st(std::mem_fun_ref(&Bar::process), bar));
Ich frage mich, ob std::for_each
es sich wirklich lohnt, wenn man bedenkt, dass ein so einfaches Beispiel bereits so viel Code erfordert.
Was denkst du über diese Angelegenheit?
c++
algorithms
Fredoverflow
quelle
quelle
map(bar.process, vec)
obwohl von einer Karte für Nebenwirkungen abgeraten wird und Listenverständnisse / Generatorausdrücke gegenüber der Karte empfohlen werden).BOOST_FOREACH
...Antworten:
Es gibt einen Grund, warum Lambdas eingeführt wurden, und das liegt daran, dass sogar das Standardkomitee erkennt, dass die zweite Form scheiße ist. Verwenden Sie das erste Formular, bis Sie C ++ 0x- und Lambda-Unterstützung erhalten.
quelle
Verwenden Sie immer die Variante, die am besten beschreibt, was Sie vorhaben. Das ist
Lassen Sie uns nun die Beispiele untersuchen:
Wir haben auch eine
for_each
- yippeh . Wir haben die[begin; end)
Reichweite, mit der wir arbeiten wollen.Im Prinzip war der Algorithmus viel expliziter und daher jeder handschriftlichen Implementierung vorzuziehen. Aber dann ... Bindemittel? Memfun? Grundsätzlich C ++ interna, wie man eine Mitgliedsfunktion bekommt? Für meine Aufgabe interessieren sie mich nicht ! Ich möchte auch nicht unter dieser wortreichen, gruseligen Syntax leiden.
Nun die andere Möglichkeit:
Zugegeben, dies ist ein häufig zu erkennendes Muster, aber ... Iteratoren erstellen, Schleifen erstellen, inkrementieren, dereferenzieren. Auch dies sind alles Dinge, die mir egal sind , um meine Aufgabe zu erledigen.
Zugegeben, es sieht viel besser aus als die erste Lösung (zumindest ist der Loop- Body flexibel und ziemlich explizit), aber es ist nicht wirklich so toll. Wir werden dieses verwenden, wenn wir keine bessere Möglichkeit hätten, aber vielleicht haben wir ...
Ein besserer Weg?
Nun zurück zu
for_each
. Wäre es nicht großartig, buchstäblich zu sagenfor_each
und flexibel in der Operation zu sein, die auch durchgeführt werden soll? Glücklicherweise sind wir seit C ++ 0x LambdasNachdem wir eine abstrakte, generische Lösung für viele verwandte Situationen gefunden haben, ist es erwähnenswert, dass es in diesem speziellen Fall einen absoluten Favoriten Nr. 1 gibt :
Klarer geht es wirklich nicht. Zum Glück ist in C ++ 0x eine ähnliche Syntax integriert !
quelle
for (const Foo& x : vec) bar.process(x);
oder verwendet,const auto&
wenn Sie möchten.const auto&
ist möglich? Wusste das nicht - tolle Infos!Weil das so unlesbar ist?
quelle
int
mit asize_t
gefährlich ist. Außerdem funktioniert die Indizierung beispielsweise nicht bei jedem Containerstd::set
.Im Allgemeinen ist die erste Form für so ziemlich jeden lesbar, der weiß, was eine for-Schleife ist, unabhängig davon, welchen Hintergrund er hat.
Auch im Allgemeinen ist der zweite überhaupt nicht so lesbar: leicht genug herauszufinden, was for_each tut, aber wenn Sie ihn noch nie gesehen haben,
std::bind1st(std::mem_fun_ref
kann ich mir vorstellen, dass er schwer zu verstehen ist.quelle
Selbst mit C ++ 0x bin ich mir nicht sicher
for_each
, ob ich viel Liebe bekommen werde.Ist viel besser lesbar.
Das einzige, was ich an Algorithmen (in C ++) nicht mag, ist, dass sie über Iteratoren argumentieren und sehr ausführliche Aussagen machen.
quelle
Wenn Sie als Funktor Bar geschrieben hätten, wäre es viel einfacher:
Hier ist der Code gut lesbar.
quelle
Ich bevorzuge letzteres, weil es ordentlich und sauber ist. Es ist eigentlich Teil vieler anderer Sprachen, aber in C ++ ist es Teil der Bibliothek. Es ist nicht wirklich wichtig.
quelle
Nun, dieses Problem ist eigentlich nicht spezifisch für C ++, würde ich behaupten.
Die Sache ist, dass das Übergeben eines Funktors in eine weitere Funktion nicht dazu gedacht ist, Schleifen zu ersetzen .
Es soll bestimmte Muster vereinfachen, die mit funktionaler Programmierung sehr natürlich werden, wie Besucher und Beobachter , um nur zwei zu nennen, die mir sofort in den Sinn kommen.
In Sprachen, denen Funktionen erster Ordnung fehlen (Java ist wahrscheinlich das beste Beispiel), erfordern solche Ansätze immer die Implementierung einer bestimmten Schnittstelle, die recht ausführlich und redundant ist.
Eine häufige Verwendung, die ich in anderen Sprachen häufig sehe, wäre:
Dies hat den Vorteil, dass Sie nicht wissen müssen, wie
someCollection
oder was tatsächlich implementiertsomeUnaryFunctor
wird. Alles, was Sie wissen müssen, ist, dass dieforEach
Methode alle Elemente der Sammlung iteriert und sie an die angegebene Funktion übergibt.Wenn Sie in der Lage sind, alle Informationen über die Datenstruktur, über die Sie iterieren möchten, und alle Informationen darüber, was Sie in jedem Iterationsschritt tun möchten, zu haben, ist ein funktionaler Ansatz eine Überkomplikation, insbesondere in einer Sprache. wo dies anscheinend ziemlich langweilig ist.
Beachten Sie auch, dass der funktionale Ansatz langsamer ist, da Sie viele Anrufe haben, die mit bestimmten Kosten verbunden sind und die Sie nicht in einer for-Schleife haben.
quelle
std::for_each(vec.begin(), vec.end(), std::bind1st(std::mem_fun_ref(&Bar::process), bar));
Und es ist entweder zur Laufzeit langsamer oder zur Kompilierungszeit (wenn Sie Glück haben). Ich verstehe nicht, warum das Ersetzen von Flusssteuerungsstrukturen durch Funktionsaufrufe den Code besser macht. Über jede hier vorgestellte Alternative ist besser lesbar.Ich denke, das Problem hier ist, dass
for_each
es sich nicht wirklich um einen Algorithmus handelt, sondern nur um eine andere (und normalerweise minderwertige) Art, eine handgeschriebene Schleife zu schreiben. Aus dieser Perspektive sollte es der am wenigsten verwendete Standardalgorithmus sein, und ja, Sie könnten wahrscheinlich genauso gut eine for-Schleife verwenden. Der Ratschlag im Titel bleibt jedoch bestehen, da es mehrere Standardalgorithmen gibt, die auf spezifischere Anwendungen zugeschnitten sind.Wo es einen Algorithmus gibt, der enger macht, was Sie wollen, dann sollten Sie Algorithmen gegenüber handgeschriebenen Schleifen bevorzugen. offensichtlichsten Beispiele sind hier das Sortieren mit
sort
oderstable_sort
oder mit der Suchelower_bound
,upper_bound
oderequal_range
, aber die meisten von ihnen haben eine Situation , in der sie bevorzugt eine Hand codiert Schleife zu verwenden , um über.quelle
Für dieses kleine Code-Snippet sind beide für mich gleichermaßen lesbar. Im Allgemeinen finde ich manuelle Schleifen fehleranfällig und bevorzuge die Verwendung von Algorithmen, aber es hängt wirklich von einer konkreten Situation ab.
quelle