Dies ist die (meiner Meinung nach) beliebteste Methode , um zu überprüfen, ob ein Wert in einem Array enthalten ist:
for (int x : array)
{
if (x == value)
return true;
}
return false;
In einem Buch, das ich vor vielen Jahren wahrscheinlich von Wirth oder Dijkstra gelesen habe, hieß es jedoch, dass dieser Stil besser ist (im Vergleich zu einer while-Schleife mit einem Ausgang im Inneren):
int i = 0;
while (i < array.length && array[i] != value)
i++;
return i < array.length;
Auf diese Weise wird die zusätzliche Exit-Bedingung ein expliziter Bestandteil der Schleifeninvariante, es gibt keine versteckten Bedingungen und Exits innerhalb der Schleife, alles ist offensichtlicher und strukturierter. Ich bevorzuge im Allgemeinen das letztere Muster, wann immer es möglich ist, und for
benutze die -Schleife, um nur von a
bis zu iterieren b
.
Und doch kann ich nicht sagen, dass die erste Version weniger klar ist. Vielleicht ist es sogar noch klarer und verständlicher, zumindest für Anfänger. Also frage ich mich immer noch, welches besser ist?
Vielleicht kann jemand eine gute Begründung für eine der Methoden geben?
Update: Hierbei handelt es sich nicht um mehrere Funktionsrückgabepunkte, Lambdas oder das Finden eines Elements in einem Array per se. Es geht darum, Schleifen mit komplexeren Invarianten als einer einzelnen Ungleichung zu schreiben.
Update: OK, ich sehe den Punkt von Leuten, die antworten und kommentieren: Ich habe hier die foreach-Schleife eingemischt, die selbst schon viel klarer und lesbarer ist als eine while-Schleife. Ich hätte das nicht tun sollen. Aber dies ist auch eine interessante Frage, also lassen wir es wie es ist: foreach-loop und eine zusätzliche Bedingung im Inneren oder eine while-Schleife mit einer expliziten Schleifeninvariante und einer Nachbedingung danach. Es scheint, dass die foreach-Schleife mit einer Bedingung und einem Exit / Break gewinnt. Ich werde eine zusätzliche Frage ohne die foreach-Schleife erstellen (für eine verknüpfte Liste).
quelle
collection.contains(foo)
Antworten:
Ich denke, für einfache Schleifen wie diese ist die Standard-First-Syntax viel klarer. Einige Leute halten Mehrfachrückgaben für verwirrend oder einen Code-Geruch, aber für ein so kleines Stück Code glaube ich nicht, dass dies ein echtes Problem ist.
Bei komplexeren Loops wird es ein bisschen fragwürdiger. Wenn der Inhalt der Schleife nicht auf Ihren Bildschirm passt und mehrere Rückgaben in der Schleife enthalten sind, muss argumentiert werden, dass die mehreren Austrittspunkte die Pflege des Codes erschweren können. Wenn Sie zum Beispiel sicherstellen müssten, dass vor dem Beenden der Funktion eine Statusverwaltungsmethode ausgeführt wurde, würde es leicht fehlen, sie einer der return-Anweisungen hinzuzufügen, und Sie würden einen Fehler verursachen. Wenn alle Endebedingungen in einer while-Schleife überprüft werden können, haben Sie nur einen Exit-Punkt und können diesen Code danach hinzufügen.
Vor allem bei Schleifen ist es jedoch gut zu versuchen, so viel Logik wie möglich in separate Methoden zu stecken. Dies vermeidet viele Fälle, in denen die zweite Methode Vorteile hätte. Lean-Loops mit klar getrennter Logik sind wichtiger als der von Ihnen verwendete Stil. Wenn der größte Teil der Codebasis Ihrer Anwendung einen Stil verwendet, sollten Sie diesen Stil beibehalten.
quelle
Das ist einfach.
Für den Leser ist fast nichts wichtiger als Klarheit. Die erste Variante fand ich unglaublich einfach und übersichtlich.
Die zweite 'verbesserte' Version musste ich mehrmals lesen und sicherstellen, dass alle Randbedingungen stimmen.
Es gibt ZERO DOUBT mit einem besseren Codierungsstil (der erste ist viel besser).
Nun - was für Menschen KLAR ist, kann von Person zu Person unterschiedlich sein. Ich bin mir nicht sicher, ob es dafür objektive Standards gibt (obwohl das Posten in einem Forum wie diesem und das Erhalten einer Vielzahl von Beiträgen von Leuten helfen kann).
In diesem speziellen Fall kann ich Ihnen jedoch sagen, warum der erste Algorithmus klarer ist: Ich weiß, wie die C ++ - Iteration über eine Containersyntax aussieht und funktioniert. Ich habe es verinnerlicht. Jemand UNFAMILIAR (seine neue Syntax) mit dieser Syntax bevorzugt möglicherweise die zweite Variante.
Aber sobald Sie diese neue Syntax kennen und verstehen, können Sie sie einfach anwenden. Bei der (zweiten) Methode der Schleifeniteration müssen Sie sorgfältig prüfen, ob der Benutzer RICHTIG nach allen Kantenbedingungen sucht, um das gesamte Array zu durchlaufen (z. B. weniger als anstelle von weniger oder gleichem Index, der für test und verwendet wird) zur Indizierung etc).
quelle
longerLength = true
, dannreturn longerLength
.length
. Wenn es tatsächlich als Array und nicht als Zeiger deklariert wurde, könnte es verwendet werdensizeof
, oder wenn es einestd::array
korrekte Member-Funktion istsize()
, gibt es keinelength
Eigenschaft.sizeof
wäre in Bytes ... Die allgemeinste seit C ++ 17 iststd::size()
.Nicht ganz. Die Variable
i
existiert hier außerhalb der while-Schleife und ist somit Teil des äußeren Gültigkeitsbereichs, während (beabsichtigtes Wortspiel)x
derfor
-Loop nur innerhalb des Gültigkeitsbereichs der Schleife existiert. Umfang ist ein sehr wichtiger Weg, um Struktur in die Programmierung einzuführen.quelle
Die beiden Schleifen haben unterschiedliche Semantik:
Die erste Schleife beantwortet einfach eine einfache Ja / Nein-Frage: "Enthält das Array das gesuchte Objekt?" Dies geschieht so kurz wie möglich.
Die zweite Schleife beantwortet die Frage: "Wenn das Array das gesuchte Objekt enthält, wie lautet der Index der ersten Übereinstimmung?" Auch dies geschieht so kurz wie möglich.
Da die Antwort auf die zweite Frage streng mehr Informationen enthält als die Antwort auf die erste, können Sie die zweite Frage beantworten und dann die Antwort auf die erste Frage ableiten. Das ist was die Linie
return i < array.length;
jedenfalls.Ich glaube, dass es normalerweise am besten ist, nur das Werkzeug zu verwenden, das zum Zweck passt sei denn, Sie können ein bereits vorhandenes, flexibleres Werkzeug wiederverwenden. Dh:
bool
ist auch in Ordnung, die erste Variante so zu ändern, dass nur eine Variable und eine Unterbrechung festgelegt werden. (Vermeidet Sekundereturn
Anweisung, die Antwort ist in einer Variablen anstelle einer Funktionsrückgabe verfügbar.)std::find
ist in Ordnung (Wiederverwendung von Code!).bool
ist jedoch nicht der Fall .quelle
Ich werde insgesamt eine dritte Option vorschlagen:
Es gibt viele verschiedene Gründe, ein Array zu durchlaufen: Überprüfen Sie, ob ein bestimmter Wert vorhanden ist, transformieren Sie das Array in ein anderes Array, berechnen Sie einen Aggregatwert, filtern Sie einige Werte aus dem Array heraus ... Wenn Sie eine einfache for-Schleife verwenden, ist dies unklar auf einen Blick speziell, wie die for-Schleife verwendet wird. Die meisten modernen Sprachen haben jedoch umfangreiche APIs in ihren Array-Datenstrukturen, die diese unterschiedlichen Absichten sehr deutlich machen.
Vergleichen Sie die Umwandlung eines Arrays in ein anderes mit einer for-Schleife:
und Verwendung einer JavaScript-ähnlichen
map
Funktion:Oder summieren Sie ein Array:
gegen:
Wie lange brauchen Sie, um zu verstehen, was dies bewirkt?
gegen
In allen drei Fällen, während die for-Schleife sicher lesbar ist, müssen Sie einige Momente verbringen, um herauszufinden, wie die for-Schleife verwendet wird, und um zu überprüfen, ob alle Zähler und Ausgangsbedingungen korrekt sind. Die modernen Funktionen im Lambda-Stil machen die Zwecke der Schleifen sehr deutlich, und Sie wissen mit Sicherheit, dass die aufgerufenen API-Funktionen korrekt implementiert sind.
Die meisten modernen Sprachen, einschließlich JavaScript , Ruby , C # und Java , verwenden diesen Stil der funktionalen Interaktion mit Arrays und ähnlichen Sammlungen.
Im Allgemeinen halte ich die Verwendung von for-Schleifen zwar nicht unbedingt für falsch, und es ist eine Frage des persönlichen Geschmacks. Ich habe mich jedoch entschieden für die Verwendung dieses Arbeitsstils mit Arrays entschieden. Dies ist insbesondere auf die größere Klarheit bei der Bestimmung der einzelnen Schleifentätigkeiten zurückzuführen. Wenn Ihre Sprache ähnliche Funktionen oder Tools in ihren Standardbibliotheken hat, empfehlen wir Ihnen, diesen Stil ebenfalls zu übernehmen!
quelle
array.find
wirft die Frage auf, da wir dann die beste Art der Implementierung besprechen müssenarray.find
. Sofern Sie keine Hardware mit integriertemfind
Betrieb verwenden, müssen wir dort eine Schleife schreiben.find
in ihren Standardbibliotheken. Zweifellos implementieren diese Bibliothekenfind
und ihre Verwandten for-Schleifen, aber das ist es, was eine gute Funktion tut: Sie abstrahiert die technischen Details vom Verbraucher der Funktion, sodass der Programmierer nicht über diese Details nachdenken muss. Obwohlfind
es wahrscheinlich mit einer for-Schleife implementiert ist, hilft es dennoch, den Code besser lesbar zu machen, und da es häufig in der Standardbibliothek enthalten ist, erhöht die Verwendung des Codes weder den Aufwand noch das Risiko.Es läuft alles auf genau das hinaus, was mit "besser" gemeint ist. Für praktische Programmierer bedeutet dies im Allgemeinen effizient, dh in diesem Fall wird durch das Verlassen der Schleife ein zusätzlicher Vergleich vermieden, und durch das Zurückgeben einer Booleschen Konstante wird ein doppelter Vergleich vermieden. das spart zyklen. Dijkstra ist mehr darum bemüht, Code zu erstellen, der sich leichter als richtig erweisen lässt . [Ich habe den Eindruck, dass die CS-Ausbildung in Europa den Nachweis der Code-Korrektheit weitaus ernster nimmt als die CS-Ausbildung in den USA, wo die wirtschaftlichen Kräfte die Codierungspraxis dominieren.]
quelle