Wie kann ich verhindern, dass ich "for" - Schleifen mit einer "if" - Bedingung innerhalb mit C++?
Mit fast allen code, den ich Schreibe, bin ich oft Umgang mit set Reduzierung der Probleme, die auf Sammlungen, die letztendlich mit naiven "wenn" - Bedingungen im inneren von Ihnen. Hier ein einfaches Beispiel:
for(int i=0; i<myCollection.size(); i++)
{
if (myCollection[i] == SOMETHING)
{
DoStuff();
}
}
Mit funktionalen Sprachen, und ich habe das problem lösen können durch die Reduzierung der Sammlung zu einer anderen Sammlung (leicht) und dann alle Vorgänge in meinem reduzierten Satz. In pseudocode:
newCollection <- myCollection where <x=true
map DoStuff newCollection
Und in anderen C-Varianten, wie z.B. C#, könnte ich reduzieren, mit einer where-Klausel wie
foreach (var x in myCollection.Where(c=> c == SOMETHING))
{
DoStuff();
}
Oder besser (zumindest für meine Augen)
myCollection.Where(c=>c == Something).ToList().ForEach(d=> DoStuff(d));
Zugegeben, ich bin dabei eine Menge von Paradigma mischen und subjektive/Meinung basiert Stil, aber ich kann mir nicht helfen, aber das Gefühl, dass mir etwas fehlt wirklich wichtig, könnte mir erlauben, verwenden Sie diese bevorzugte Technik mit C++. Könnte jemand mich aufklären?
- Aus der C++ standard-library-Funktionen, können Sie versuchen
std::copy_if
sein, aber die Auswahl nicht faul - wenn es ok ist für Sie, eine Kopie zu machen, möchten Sie vielleicht werfen Sie einen Blick auf std::copy_if
- Sie können daran interessiert sein, Reihe-v3. Sollte es auch dann kommen, um C++ als TS und hoffentlich standardisierte in einer zukünftigen Version.
c++11
hat lambda-Ausdrücke, so dass möglicherweise etwas, das Sie möchten, um zu sehen wie auch.- Ich schlage vor, Sie ändern den Titel, weil beide Antworten verwendet, die Schleifen
- Könnte Sie klären, ob Sie interessiert sind, indem Sie die performance-Aspekte, oder allein durch die syntaktischen Aspekte?
- Soweit ich weiß, dass C# - code wird nicht kompiliert.
:
ersetzt werden durchin
- Hinzufügen @NullException Kommentar, der andere C# - snippet auch nicht kompilieren, weil es bewusst keine
ForEach
imIEnumerable
- Ich fühle die Notwendigkeit zu betonen, dass der
if
innerhalb einerfor
Sie erwähnen, ist nicht nur ziemlich funktional gleichwertig zu den anderen Beispielen würde aber auch wahrscheinlich schneller in eine Menge von Fällen. Auch für jemanden, der behauptet, wie funktionalen Stil, was Sie fördern scheint zu gehen gegen die funktionale Programmierung das Geliebte Konzept von Reinheit, daDoStuff
hat eindeutig side effcets. - Wenn Sie das tun, viele, viele Male mit den gleichen filter-Bedingungen, die ich vorschlagen würde, mit so etwas wie Boost multi_index, die pflegen können viele Indizes auf die gleichen Daten. Es ist wie mit Ihrem eigenen DB in der app.
- Wenn Sie können, ich denke, man kann Sortieren Sie Ihre Daten zu ersetzen, der für"+", wenn mit nur einer for-Schleife iteriert über die Individuen, die hinsichtlich Ihrer Bedingungen (oder zwei Schleifen im Falle eines anderen, und so weiter)
- Ich habe nie wirklich verstanden, warum die Menschen verbinden, die alle Logik auf einer einzigen Zeile macht es sich irgendwie besser oder mehr lesbar. C++ - snippet an der Spitze ist bei weitem die meisten lesbar für mich aus all Ihre Möglichkeiten. Und da der Wirkungsgrad nicht verändert wird, kann ich nicht verstehen, warum Sie lieber nicht zu schreiben, dass, es sei denn, Sie werden bezahlt nach der Anzahl der code-Zeilen, die Sie löschen.
- Ich, die zweite. Ich denke, die for-Schleife mit der internen, wenn die mit Abstand am meisten lesbar. Ich bin mir nicht sicher über die Effizienz, mit der Ausnahme, dass es wohl wirklich schließen. Immer eine Untergruppe und dann die Iteration auf die Teilmenge der logisch erfordert zwei traversalen statt, und es ist immer noch der test für jedes element, so dass ich lieber zu durchqueren es einmal und explizit zeigen die Tests, dass das passieren wird sowieso für jedes element. Es ist (gerade noch) effizienter als die Art und Weise, und sehr viel transparenter.
- Vereinbart: es ist nur syntaktischer Zucker. Und die Frage, der Titel ist irreführend, denn es ist sehr unterschiedlich zu vermeiden Verzweigung und versteckt es unter Abstraktion.
- Heute ist die Effizienz nicht geändert. Morgen - vielleicht. Wenn Sie " Anforderungsfilterung ersten, dann einen Bereich für dann - ich bin, darüber zu spekulieren, hier - filtern könnten parallel ausgeführt werden (je nach Bibliothek) und könnte schneller sein (je nach Architektur). Wissen wir nicht - aber wir wollen nicht ausschließen.
- Ein Vorteil in der Verwendung von Dingen wie
map
/filter
zu iterieren über Sammlungen ist, dass Sie wissen, was Sie tun werden. Eine generische Schleife tun können, alle Arten von komplexen Zeug und es ist viel einfacher, auf die Einführung eines bug manuell schreiben derfor
Schleife als schreibenmap doStuff collection
. Dies kann auch helfen, den compiler bei der Optimierung. Das gleiche (mehr) wahr schreiben Sie die rekursive Funktion, die per hand oder mit so etwas wie einemfold
/reduce
statt. - Ich bin überrascht, dass niemand darauf hingewiesen, dass boost bedeutet dies: coliru.stacked-crooked.com/a/f2274ee265d19a1a
- Wenn der compiler bekommt ausreichend schlau, dass er die erstaunliche Optimierungen, die Sie sich ausdenken, @lorro, sollte es sicherlich schlau genug, zu erkennen, gemeinsame Muster und transformieren Sie Sie so ausführen kann, dass es die gleichen Optimierungen. Die machen es wichtiger, code zu schreiben, in der normalen, offensichtlichen Weg. Vorausgesetzt natürlich, dass alle diese Optimierungen sind auch möglich, da Sie sehr wahrscheinlich brechen Bestellung von Sicherheiten, die durch die language-Spezifikation.
- Ich Rede nicht von dem compiler, ich Rede von der Bibliothek, die
filter()
. Wir sind auf Vereinbarung, dass Sie nicht-trivial für einen compiler (und es ist nicht trivial, ob das getan werden sollte, in den ersten Platz), jedoch mitstd::async()
es ist jetzt fast trivial zu schreibenfilter()
hat, der die Filterung im hintergrund (klar, die Angabe dieser in der Dokumentation). Solange beide Ihren filter und loop-Körper sind ausreichend lang und können parallel ausgeführt werden, könnte dies beschleunigen Sie Ihren code ein wenig Recht. - es ist nicht unbedingt über code-Zeilen, ist es auch egal, ob Sie lieber in Operationen auf einem Bereich, oder lieber eine Schleife über einen Bereich, der explizit die Durchführung der gleichwertige operation auf jedes element. Auch ohne die bedingte, und noch bevor der range-basierte for-Schleifen, einige Leute immer noch nicht
std::for_each
wenn Sie hatte eine Pistole an Ihren Kopf, auf der Grundlage, dass die Schleife mehr lesbar sind, um Sie in allen Fällen. In der Erwägung, dass anderen Menschen gefallenfor_each
so viel, dass Sie standardisierte it 😉 - Wie der OP sagt, der Wunsch, zu entfernen die explizite
if
folgt aus einem funktionalen Stil, c.f. degoes.net/articles/destroy-all-ifs - Möglich, Duplikat der Wie kombiniert man eine Funktion und ein Prädikat in for_each?
Du musst angemeldet sein, um einen Kommentar abzugeben.
IMHO ist es mehr straight forward und lesbarer ist die Verwendung einer for-Schleife mit einer if drin. Allerdings, wenn das ist ärgerlich für Sie, könnten Sie ein
for_each_if
wie folgt:Anwendungsfall:
Live Demo
v
und verwendenstd::begin
undstd::end
imfor_each_if
Funktion?range
von Vorlagen geben, die erforderlich ist, umbegin()
undend()
(entweder eigene oder externe) und bieten einerange(Iter first, Iter second)
für die Kompatibilität mit Iteratoren.end()
's von zwei Vektoren).would it be possible to instead just pass in v and use std::begin and std::end in the for_each_if function?
: Sorry, habe ich falsch interpretiert, die Frage. Nein das ist nicht möglich und, wie andere vorgeschlagen, es wäre nicht flexibel.find_if
undfind
ob Sie arbeiten reicht oder Paare von Iteratoren. (Es gibt ein paar Ausnahmen, wiefor_each
undfor_each_n
). Der Weg zu vermeiden, schreiben neue algos für jedes niesen ist die Verwendung von verschiedenen Operationen mit den vorhandenen algos, e.g stattfor_each_if
einbetten der Bedingung in das callable übergebenfor_each
z.B.for_each(first, last, [&](auto& x) { if (cond(x)) f(x); });
find_if
verwenden Sie einfachfind
und Kette es mitfilter
, das gleiche gilt für alle anderen_if
Variantenfilter
erzeugt einen view (also faul wertet einige andere Bereich), und nehmen Sie nur das erste element. Aber das wird off-topic für diese Antwortstd::copy_if
,std::for_each
), und die Umsetzung ist unkompliziert.Boost bietet Bereiche an, die verwendet werden können, w/range-basierte for. Bereiche haben den Vorteil, dass Sie nicht kopieren Sie die zugrunde liegende Datenstruktur, Sie sind nur einen 'Blick' (das ist
begin()
,end()
für den Bereich undoperator++()
,operator==()
für den iterator). Dies könnte Ihr Interesse: http://www.boost.org/libs/range/doc/html/range/reference/adaptors/reference/filtered.htmlis_even
=>condition
,input
=>myCollection
etc.filtered()
für Sie - das heißt, es ist besser, verwenden Sie eine unterstützte lib als einige ad-hoc code.boost::adaptors
sicher sein, eigenen Bereich (d.h., don ' T pass eine vorübergehende). Dies ist besonders schwierig, wenn Sie möchten mehrere Adapter. Dies bedeutet nicht, dassboost::adaptors
ist falsch, es ist nur, dass Sie vorsichtig sein müssen. Finden Sie diese Fehler für weitere Informationen: svn.boost.org/trac/boost/ticket/7630Anstatt einen neuen Algorithmus, der die akzeptierte Antwort ist, die Sie verwenden können, einen vorhandenen mit einer Funktion, gilt die Bedingung:
Oder wenn Sie wirklich wollen, einen neuen Algorithmus, der mindestens Wiederverwendung
for_each
dort, statt Sie zu duplizieren die iteration Logik:std::for-each(first, last, [&](auto& x) {if (p(x)) op(x); });
ist ganz einfacher alsfor (Iter x = first; x != last; x++) if (p(x)) op(x);}
?std::for_each(std::execution::par, first, last, ...);
Wie einfach ist es, diejenigen hinzuzufügen, die die Dinge auf eine handgeschriebene Schleife?Die Idee der Vermeidung von
Konstrukte wie ein antipattern ist zu breit.
Ist es völlig in Ordnung zu verarbeiten mehrerer Gegenstände, die übereinstimmung mit einem bestimmten Ausdruck innerhalb einer Schleife, und den code nicht bekommen, viel klarer als das. Wenn die Verarbeitung wächst zu groß für Bildschirm, ist das ein guter Grund für die Verwendung einer Unterroutine, aber immer noch die bedingte ist am besten platziert, der innerhalb der Schleife, d.h.
ist weitaus vorzuziehen
Wird es ein antipattern, wenn nur einem element übereinstimmen, denn dann wäre es übersichtlicher für das erste Suche nach dem element, und führen Sie die Verarbeitung außerhalb der Schleife.
ist ein extremes und offensichtliches Beispiel HIERFÜR. Subtiler, und damit häufiger, ist ein factory-Muster wie
Dies ist schwer zu Lesen, weil es nicht offensichtlich ist, dass der Körper-code ausgeführt werden, sobald nur. In diesem Fall wäre es besser, sich zu trennen-Suche:
Gibt es noch eine
if
innerhalb einerfor
, aber aus dem Kontext wird deutlich, was es bedeutet, es gibt keine Notwendigkeit zur änderung dieses Codes, es sei denn, die lookup-änderungen (z.B. zu einemmap
), und es ist sofort klar, dasscreate_object()
nur einmal aufgerufen wird, weil es nicht in einer Schleife.for( range ){ if( condition ){ action } }
-Stil macht es leicht zu Lesen, was ein Stück zu einem Zeitpunkt, und verwendet nur Kenntnisse der grundlegenden Sprachkonstrukte.for(...) if(...) { ... }
ist oft die beste Wahl (das ist, warum ich qualifiziert die Empfehlung zur Aufteilung der Handlung in ein Unterprogramm).for(…)if(…)…
wenn es der einzige Ort lookup aufgetreten.Hier ist eine schnelle relativ gering
filter
Funktion.Dauert es ein Prädikat. Gibt es eine function-Objekt, das nimmt ein durchsuchbar.
Gibt es einen iterierbar, die verwendet werden können in einer
for(:)
Schleife.Ich nahm kurze Schnitte. Eine echte Bibliothek sollte machen echte Iteratoren, nicht die
for(:)
-qualifying pseudo-fascades ich.Am point-of-Nutzung, sieht es wie folgt aus:
ist ziemlich nett, und druckt
Live-Beispiel.
Es ist ein Vorschlag für die Ergänzung zu C++ namens Rangesv3, die nicht diese Art der Sache und mehr.
boost
hat auch filter reicht/Iteratoren zur Verfügung. boost hat auch Helfer, die das machen, Schreibe viel kürzer.Einen Stil, bekommt genug zu erwähnen, aber noch nicht erwähnt wurde noch, ist:
Vorteile:
DoStuff();
wenn Bedingung Komplexität erhöht. Logisch,DoStuff();
sollten auf der obersten Ebene derfor
Schleife, und es ist.SOMETHING
s der Sammlung, ohne dass der Leser, um zu überprüfen, dass es nichts nach dem schließen}
desif
block.Nachteile:
continue
wie die anderen flow-control-statements, wird missbraucht in einer Weise, die dazu führen, hard-to-follow-code, so viel, dass manche Menschen im Gegensatz zu alle Verwendung: es gibt einen gültigen Stil von Codierung, dass einige Folgen, die vermeidet, dasscontinue
, vermeidetbreak
anders als in einerswitch
, vermeidetreturn
andere als am Ende einer Funktion.for
- Schleife, die läuft auf vielen Linien, ein zwei-line ", wenn nicht, weiter" ist viel klarer, logischer und lesbarer. Sofort sagen: "überspringen Sie dies, wenn" nach derfor
Aussage liest sich gut und, wie Sie sagten, nicht Einrücken, die restlichen funktionalen Aspekte der Schleife. Wenn diecontinue
ist weiter unten, jedoch einige Klarheit geopfert wird (d.h. wenn eine operation immer durchgeführt werden, bevor dieif
- Anweisung).Sieht ziemlich viel wie ein C++-spezifische
for
Verständnis für mich. Sie?auto const
hat keinerlei Einfluss auf iteration um. Wenn Sie nach oben schauen, reichte-basiertefor
Sie werden sehen, dass es im Grunde genommen ein standard-loop ausbegin()
zuend()
mit impliziten Dereferenzierung. Es gibt keine Möglichkeit, es zu brechen könnte die Bestellung von Sicherheiten (falls vorhanden) des Containers iteriert über; es wäre schon gelacht Antlitz der Erdestd::future
sstd::function
s, auch diejenigen, anonyme Verschlüsse sind sehr gut C++ - ish, die in der syntax; jede Sprache hat Ihre eigene Ausdrucksweise, und wenn incorporatin neuen features, die es versucht zu machen, imitieren die alten bekannten syntax.for
änderungen Verhalten im Vergleich zu OP ist indiziert Schleife, die ist sehr unwahrscheinlich: wenn ein compiler aus dieser byzantinischen transformation für range -for
ist, wäre es ebenso frei, dies zu tun für eine indizierte Schleife.for
Verständnis", insbesondere "Verständnis"?Wenn DoStuff() würde davon abhängig sein, ob ich irgendwie in die Zukunft dann würde ich vorschlagen, dies garantiert Zweig-gratis-bit-Maskierung Variante.
Wo popcount ist keine Funktion dabei, eine population count ( Anzahl der bits = 1 ). Es wird eine gewisse Freiheit zu setzen, die mehr fortgeschrittene Einschränkungen mit ich und Ihre Nachbarn. Wenn das nicht erforderlich sein, können wir Streifen die innere Schleife und ein remake der äußeren Schleife
gefolgt von einem
Auch, wenn Sie nicht kümmern, die Neuordnung der Sammlung, std::partition ist Billig.
std::partition
verschiebt den container.Ich bin in Ehrfurcht vor der Komplexität der oben genannten Lösungen. Ich wollte vorschlagen eine einfache
#define foreach(a,b,c,d) for(a; b; c)if(d)
aber es hat einige offensichtliche Defizite, zum Beispiel, müssen Sie daran denken, Kommata statt Semikolons in der Schleife, und Sie können nicht den Komma-operator ina
oderc
.Andere Lösung bei der ich:s wichtig sind. Diese baut man eine Liste, die füllt die Indizes, von denen zu nennen doStuff() an. Wieder einmal der wichtigste Punkt ist zu vermeiden, die Verzweigung und den Handel für pipelineable arithmetische Kosten.
Den "magischen" Zeile den Puffer laden Zeile, überträgt sich rein rechnerisch ermittelt, ob zu halten, den Wert und bleiben in der position oder count-up-position und Wert hinzufügen. So handeln wir entfernt eine potenzielle Niederlassung für einige Logik-und Arithmetik-und vielleicht einige cache-hits. Ein typisches Szenario, wenn dies hilfreich wäre ist, wenn doStuff() wird eine kleine Menge von pipelineable Berechnungen und in jeder Filiale zwischen Anrufe unterbrechen könnten diese pipelines.
Dann einfach Schleife über den Puffer und führen Sie doStuff (), bis wir zu cnt. Dieses mal werden wir den Strom i im Puffer gespeichert, so können wir es verwenden im Aufruf von doStuff (), wenn wir müssten.