Ci sono dei vantaggi nell'usare questa variabile extra nell'annotazione del ciclo for?


11

Ho trovato la seguente annotazione in loop in un grande progetto a cui sto lavorando (pseudocodice):

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

Ciò che ha attirato la mia attenzione è questa variabile "n" aggiuntiva. Non ho mai visto un for lop scritto in questo modo prima d'ora.

Ovviamente in questo scenario non c'è motivo per cui questo codice non possa essere scritto nel modo seguente (al quale sono molto abituato):

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

Ma mi ha fatto pensare.

C'è uno scenario in cui scrivere un tale ciclo for avrebbe senso? L'idea mi viene in mente che la lunghezza della "matrice" può cambiare durante l'esecuzione del ciclo for, ma non vogliamo fare un loop oltre la dimensione originale, ma non riesco a immaginare un simile scenario.

Anche la riduzione dell'array all'interno del ciclo non ha molto senso, perché è probabile che si ottenga OutOfBoundsException.

Esiste un modello di progettazione noto in cui questa annotazione è utile?

Modifica Come notato da @ Jerry101 il motivo è la prestazione. Ecco un link al test delle prestazioni che ho creato: http://jsperf.com/ninforloop . A mio avviso, la differenza non è abbastanza grande a meno che non si stia ripetendo un array molto grande. Il codice da cui l'ho copiato aveva solo un massimo di 20 elementi, quindi penso che la leggibilità in questo caso superi la considerazione delle prestazioni.


In reazione alla tua modifica: questa è una programmazione orientata agli oggetti. Array.length standard è veloce ed economico. Ma per qualsiasi motivo, l'implementazione di .length potrebbe cambiare in qualcosa di costoso. Se un valore rimane lo stesso, non ottenerlo più volte.
Pieter B,

Sono d'accordo. È utile conoscere questa opzione, ma probabilmente non la userei molto spesso, a meno che non ottenga un esempio simile alla tua query sql. Grazie☺
Vlad Spreys il

Il fatto è che se il tuo array è un milione di elementi, chiamerai array.length un milione di volte invece di 1 volta mentre il valore rimane lo stesso. Chiedere qualcosa un milione di volte e sapere che ogni risposta successiva sarà la stessa della prima risposta ...... mi sembra un po 'insensata.
Pieter B,

1
Non trovo che questo renda il codice significativamente più difficile da leggere. (Seriamente, se qualcuno ha problemi con un ciclo così semplice, dovrebbero essere preoccupati.) Ma: 1) Sarei terribilmente sorpreso se dopo 4 decenni di lingue discendenti in C e C, ogni compilatore serio non lo avesse già fatto per te; e 2) questo potrebbe non essere un punto caldo. Non sembra valere la pena di spendere altri 5 secondi in più per analizzarlo a meno che non sia all'interno di una libreria (ad esempio l'implementazione di una struttura di dati.)
Doval

1
In C # /. NET la variante che utilizza npotrebbe essere più lenta della variante che utilizza array.Lengthpoiché JITter potrebbe non notare che potrebbe eliminare i controlli dei limiti di array.
Codici A Caos il

Risposte:


27

La variabile ngarantisce che il codice generato non recuperi la lunghezza dell'array per ogni iterazione.

È un'ottimizzazione che potrebbe fare la differenza nel tempo di esecuzione a seconda della lingua utilizzata, se l'array è effettivamente un oggetto di raccolta o un "array" JavaScript e altri dettagli di ottimizzazione.


3
No, non lo fa. Se la lunghezza dell'array viene recuperata ogni iterazione dipende non solo dal linguaggio, ma anche dalle opzioni del compilatore e del compilatore e da ciò che viene fatto nel ciclo (sto parlando di C e C ++)
BЈовић

.. E anche così, metterei personalmente il compito n appena sopra il ciclo se lo volessi davvero, poiché - come notato da OP - questo è un raro costrutto (YMMV) da usare ed è facilmente perso / non compreso dalla lettura di altri sviluppatori il codice.
Cwap

20
Come scritto, si estende nal ciclo, che in genere è una buona cosa. Lo definirei prima del ciclo solo se volessi riutilizzarlo più tardi.
Jerry101,

4
@ Jerry101: A quanto pare lo snippet di esempio è JavaScript, dove non esiste un ambito loop ...
Bergi,

1
@BЈовић: in pratica, il compilatore raramente riesce a dimostrare che la dimensione dell'array non varia (in genere è banalmente dimostrabile solo se il vettore su cui stai eseguendo il loop è locale alla funzione e stai chiamando solo funzioni incorporate nel loop).
Matteo Italia,

2

Il recupero della lunghezza dell'array può essere facilmente "più costoso" dell'azione effettiva su cui si ripete.

Quindi se il set non cambia, interroga la lunghezza solo una volta.

Non con gli array, ma con i set di record provenienti da un server sql ho visto notevoli miglioramenti non interrogando il conteggio dei record in ogni iterazione. (ovviamente fallo solo se puoi garantire che l'array o il set di record non vengano modificati durante questo processo).


Se il conteggio dei record cambia, quindi cosa? Supponi che ci siano 100 articoli e mentre stai elaborando l'articolo 50, viene inserito un articolo dopo # 25. Quindi, quando elabori l'articolo # 51, è lo stesso che # 50 che hai già elaborato e le cose vanno male. Quindi il problema non è che la lunghezza cambi, è molto più pervasiva.
gnasher729,

@ gnasher729 una volta entrati in un comportamento asimmetrico c'è molto che può andare storto. Ma ci sono cose che puoi fare contro questo come l'avvio e la transazione sql.
Pieter B,

2

L'idea mi viene in mente che la lunghezza della "matrice" può cambiare durante l'esecuzione del ciclo for, ma non vogliamo fare un loop oltre la dimensione originale, ma non riesco a immaginare un simile scenario.

Le persone che parlano delle prestazioni probabilmente hanno ragione sul fatto che è per questo che è stato scritto in quel modo (la persona che l'ha scritto in quel modo potrebbe non essere corretta e influire in modo significativo sulle prestazioni, ma è un'altra cosa).

Tuttavia, per rispondere a questa parte della tua domanda, penso che sia più probabile che accada quando lo scopo principale del loop è modificare l'array su cui si sta eseguendo il loop. Prendi in considerazione qualcosa del genere, in pseudo-codice:

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());
    }
}

Naturalmente ci sono altri modi per scriverlo. In questo caso, avrei potuto controllare i più contemporaneamente verificando se i destinatari accettavano i loro inviti e producendo l'elenco completo degli ospiti un invito alla volta. Non voglio necessariamente combinare queste due operazioni, ma non riesco immediatamente a pensare a un motivo per cui questo è decisamente sbagliato e quindi è richiesto questo design . È un'opzione da considerare, di tanto in tanto.

In realtà penso che la cosa più sbagliata con questo codice sia che l'appartenenza alla guestsmatrice significhi cose diverse in punti diversi del codice. Quindi certamente non lo definirei un "modello", ma non so che è abbastanza un difetto escludere di fare qualsiasi cosa di questo tipo :-)


L'array può anche essere modificato da un altro thread. Ciò impedisce al compilatore di ottimizzare il recupero della lunghezza. Quindi, in questione, il programmatore dice esplicitamente che l'array non cambia anche in altri thread.
Basilevs,

0

Se possibile, è necessario utilizzare un iteratore che attraversa tutti gli elementi dell'array. Si utilizza l'array solo come una sequenza, non come memoria ad accesso casuale, quindi sarebbe ovvio che un iteratore che esegue semplicemente un accesso sequenziale sarebbe più veloce. Immagina che ciò che pensi sia un array in realtà è un albero binario, ma qualcuno ha scritto un codice che determina la "lunghezza" contando gli elementi e il codice che accede all'i-esimo elemento, anche attraversando l'elemento, ognuno in O (n ). Un iteratore può essere molto veloce, il tuo loop sarà sloooooow.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.