Gibt es Vorteile bei der Verwendung dieser zusätzlichen Variablen in der for-Schleifenanmerkung?

11

Ich habe die folgende Schleifenanmerkung in einem großen Projekt gefunden, an dem ich arbeite (Pseudocode):

var someOtherArray = [];
for (var i = 0, n = array.length; i < n; i++) {
    someOtherArray[i] = modifyObjetFromArray(array[i]);
}

Was meine Aufmerksamkeit auf sich gezogen hat, ist diese zusätzliche "n" -Variable. Ich habe noch nie einen so geschriebenen for lop gesehen.

Offensichtlich gibt es in diesem Szenario keinen Grund, warum dieser Code nicht folgendermaßen geschrieben werden konnte (was ich sehr gewohnt bin):

var someOtherArray = [];
for (var i = 0; i < array.length; i++) {
    someOtherArray[i] = modifyObjetFromArray(array[i]);
}

Aber es hat mich zum Nachdenken gebracht.

Gibt es ein Szenario, in dem das Schreiben einer solchen for-Schleife sinnvoll wäre? Die Idee kommt in den Sinn, dass sich die "Array" -Länge während der Ausführung der for-Schleife ändern kann, aber wir möchten nicht weiter als bis zur ursprünglichen Größe schleifen, aber ich kann mir ein solches Szenario nicht vorstellen.

Das Verkleinern des Arrays innerhalb der Schleife ist ebenfalls nicht sehr sinnvoll, da wir wahrscheinlich OutOfBoundsException erhalten.

Gibt es ein bekanntes Entwurfsmuster, bei dem diese Anmerkung nützlich ist?

Bearbeiten Wie von @ Jerry101 angegeben, ist der Grund die Leistung. Hier ist ein Link zu dem von mir erstellten Leistungstest: http://jsperf.com/ninforloop . Meiner Meinung nach ist der Unterschied nicht groß genug, es sei denn, Sie iterieren durch ein sehr großes Array. Der Code, von dem ich ihn kopiert habe, hatte nur bis zu 20 Elemente, daher überwiegt meiner Meinung nach die Lesbarkeit in diesem Fall die Leistungsaspekte.

Vlad Spreys
quelle
Als Reaktion auf Ihre Bearbeitung: Dies ist eine objektorientierte Programmierung. Standard array.length ist schnell und billig. Aber aus irgendeinem Grund könnte sich die Implementierung von .length in etwas Teueres ändern. Wenn ein Wert gleich bleibt, erhalten Sie ihn nicht mehrmals.
Pieter B
Genau. Es ist gut, über diese Option Bescheid zu wissen, aber ich würde sie wahrscheinlich nicht sehr oft verwenden, es sei denn, ich erhalte ein ähnliches Beispiel für Ihre SQL-Abfrage. Vielen Dank
Vlad Spreys
Wenn Ihr Array aus einer Million Elementen besteht, rufen Sie array.length millionenfach statt einmal auf, während der Wert gleich bleibt. Eine Million Mal etwas zu fragen und zu wissen, dass jede nachfolgende Antwort dieselbe ist wie die erste Antwort ...... klingt für mich nur ein bisschen unsinnig.
Pieter B
1
Ich finde nicht, dass dies das Lesen des Codes erheblich erschwert. (Im Ernst, wenn jemand Probleme mit einer so einfachen Schleife hat, sollte er sich Sorgen machen.) Aber: 1) Ich wäre schrecklich überrascht, wenn nach 4 Jahrzehnten von C- und C-abstammenden Sprachen nicht jeder ernsthafte Compiler dies bereits getan hätte für dich; und 2) dies ist möglicherweise kein Hot Spot. Es scheint nicht wert zu sein, jemanden dazu zu bringen, zusätzliche 5 Sekunden damit zu verbringen, es zu analysieren, es sei denn, es befindet sich in einer Bibliothek (z. B. die Implementierung einer Datenstruktur)
Doval
1
In C # /. NET ist die verwendete Variante nmöglicherweise langsamer als die verwendete Variante, array.Lengthda der JITter möglicherweise nicht bemerkt, dass er die Überprüfung der Array-Grenzen eliminieren kann.
CodesInChaos

Antworten:

27

Die Variable nstellt sicher, dass der generierte Code nicht bei jeder Iteration die Array-Länge abruft.

Es handelt sich um eine Optimierung, die je nach verwendeter Sprache einen Unterschied in der Laufzeit bewirken kann, unabhängig davon, ob das Array tatsächlich ein Sammlungsobjekt oder ein JavaScript- "Array" ist, sowie andere Optimierungsdetails.

Jerry101
quelle
3
Nein, das tut es nicht. Ob die Länge des Arrays bei jeder Iteration abgerufen wird, hängt nicht nur von der Sprache ab, sondern auch von den Compiler- und Compileroptionen und den
Vorgängen
.. Und trotzdem würde ich die n-Zuweisung persönlich direkt über die Schleife setzen, wenn ich es wirklich wollte, da dies - wie OP bemerkte - ein seltenes (YMMV) Konstrukt ist, das verwendet werden kann und von anderen Entwicklern leicht übersehen / nicht verstanden wird der Code.
Cwap
20
Wie geschrieben, erstreckt es sich nauf die Schleife, was normalerweise eine gute Sache ist. Ich würde es nur vor der Schleife definieren, wenn ich es später wieder verwenden wollte.
Jerry101
4
@ Jerry101: Anscheinend ist das Beispiel-Snippet JavaScript, wo es keinen Loop-Bereich gibt ...
Bergi
1
@ BЈовић: In der Praxis kann der Compiler selten nachweisen, dass die Arraygröße nicht variiert (normalerweise ist dies nur dann trivial nachweisbar, wenn der Vektor, über den Sie eine Schleife ausführen, lokal für die Funktion ist und Sie nur inline-Funktionen in der Schleife aufrufen).
Matteo Italia
2

Das Abrufen der Länge des Arrays kann leicht "teurer" sein als die tatsächliche Aktion, über die Sie iterieren.

Wenn sich die Menge nicht ändert, fragen Sie die Länge nur einmal ab.

Nicht mit Arrays, sondern mit Datensatzgruppen, die von einem SQL-Server stammen. Ich habe dramatische Verbesserungen festgestellt, indem ich nicht bei jeder Iteration die Anzahl der Datensätze abgefragt habe. (Tun Sie dies natürlich nur, wenn Sie garantieren können, dass Ihr Array oder Datensatz während dieses Vorgangs nicht geändert wird).

Pieter B.
quelle
Wenn sich die Anzahl der Datensätze ändert, was dann? Angenommen, es gab 100 Elemente, und während Sie Element 50 verarbeiten, wird ein Element nach # 25 eingefügt. Wenn Sie also Element Nr. 51 verarbeiten, ist es dasselbe wie Nr. 50, das Sie bereits verarbeitet haben, und es geht schief. Das Problem ist also nicht, dass sich die Länge ändert, sondern dass sie viel weiter verbreitet ist.
Gnasher729
@ gnasher729 Sobald Sie in asymmetrisches Verhalten geraten, kann eine Menge schief gehen. Aber es gibt Dinge, die Sie dagegen tun können, wie das Starten und die SQL-Transaktion.
Pieter B
2

Die Idee kommt in den Sinn, dass sich die "Array" -Länge während der Ausführung der for-Schleife ändern kann, aber wir möchten nicht weiter als bis zur ursprünglichen Größe schleifen, aber ich kann mir ein solches Szenario nicht vorstellen.

Die Leute, die über Leistung sprechen, haben wahrscheinlich Recht, dass dies der Grund ist, warum es so geschrieben wurde (die Person, die es so geschrieben hat, ist möglicherweise nicht richtig, dass es die Leistung erheblich beeinflusst, aber das ist eine andere Sache).

Um diesen Teil Ihrer Frage zu beantworten, ist es meiner Meinung nach am wahrscheinlichsten, wenn der Hauptzweck der Schleife darin besteht, das Array zu ändern, über das sie eine Schleife durchläuft. Betrachten Sie so etwas im Pseudocode:

guests = [];
for (i = 0; i < invitations.length; ++i) {
    if (! invitations[i].is_declined()) {
        guests.append(invitations[i].recipient())
    }
}
// maybe some other stuff to modify the guestlist
for (i = 0, n = guests.length; i < n; ++i) {
    if (guests[i].has_plus_one()) {
        guests.append(guests[i].get_plus_one());
    }
}

Natürlich gibt es auch andere Möglichkeiten, es zu schreiben. In diesem Fall hätte ich gleichzeitig mit der Überprüfung, ob die Empfänger ihre Einladungen angenommen haben, nach Plus suchen und die vollständige Gästeliste nacheinander erstellen können. Ich möchte diese beiden Operationen nicht unbedingt kombinieren, aber ich kann mir nicht sofort einen Grund vorstellen, warum das definitiv falsch ist, und daher ist dieses Design erforderlich . Es ist eine Option, die gelegentlich in Betracht gezogen werden sollte.

Eigentlich denke ich, dass das, was an diesem Code am meisten falsch ist, ist, dass die Mitgliedschaft im guestsArray an verschiedenen Stellen im Code verschiedene Dinge bedeutet. Daher würde ich es sicherlich nicht als "Muster" bezeichnen, aber ich weiß nicht, dass es ein ausreichender Defekt ist, um auszuschließen, jemals etwas in dieser Art zu tun :-)

Steve Jessop
quelle
Das Array kann auch von einem anderen Thread geändert werden. Dies verhindert, dass der Compiler den Längenabruf optimiert. In Frage stellt der Programmierer ausdrücklich, dass sich das Array auch in anderen Threads nicht ändert.
Basilevs
0

Wenn möglich, sollten Sie einen Iterator verwenden, der alle Elemente des Arrays durchläuft. Sie verwenden das Array nur als Sequenz und nicht als Speicher mit wahlfreiem Zugriff. Es ist daher offensichtlich, dass ein Iterator, der nur einen sequentiellen Zugriff ausführt, schneller ist. Stellen Sie sich vor, was Sie denken, dass ein Array tatsächlich ein Binärbaum ist, aber jemand hat Code geschrieben, der die "Länge" durch Zählen der Elemente bestimmt, und Code, der auf das i-te Element zugreift, auch durch Durchlaufen des Elements, jeweils in O (n) ). Ein Iterator kann sehr schnell sein, Ihre Schleife wird langsam sein.

gnasher729
quelle