¿Hay algún beneficio de usar esta variable adicional en la anotación for loop?

11

He encontrado la siguiente anotación de bucle en un gran proyecto en el que estoy trabajando (pseudocódigo):

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

Lo que me llamó la atención es esta variable "n" adicional. Nunca antes había visto un for lop escrito de esta manera.

Obviamente, en este escenario, no hay ninguna razón por la cual este código no se pueda escribir de la siguiente manera (a lo que estoy muy acostumbrado):

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

Pero me dejó pensando.

¿Hay algún escenario en el que escribir un bucle for tenga sentido? Se me ocurre la idea de que la longitud de la "matriz" puede cambiar durante la ejecución del bucle for, pero no queremos hacer un bucle más allá del tamaño original, pero no puedo imaginar tal escenario.

Reducir la matriz dentro del bucle tampoco tiene mucho sentido, porque es probable que obtengamos OutOfBoundsException.

¿Existe un patrón de diseño conocido donde esta anotación sea útil?

Editar Como señaló @ Jerry101, la razón es el rendimiento. Aquí hay un enlace a la prueba de rendimiento que he creado: http://jsperf.com/ninforloop . En mi opinión, la diferencia no es lo suficientemente grande a menos que esté iterando a través de una matriz muy grande. El código del que lo copié solo tenía hasta 20 elementos, por lo que creo que la legibilidad en este caso supera la consideración de rendimiento.

Vlad Spreys
fuente
En reacción a la edición: esta es una programación orientada a objetos. La longitud estándar de la matriz es rápida y barata. Pero por alguna razón, la implementación de .length podría cambiar a algo costoso. Si un valor permanece igual, no lo obtenga varias veces.
Pieter B
Estoy de acuerdo. Es bueno saber acerca de esta opción, pero probablemente no la usaría muy a menudo, a menos que obtenga un ejemplo similar a su consulta SQL. Gracias☺
Vlad Spreys
La cosa es que si su matriz es un millón de elementos, va a llamar a array.length un millón de veces en lugar de 1 vez mientras el valor permanezca igual. Preguntar algo un millón de veces y saber que cada respuesta posterior será la misma que la primera ... simplemente me parece un poco absurdo.
Pieter B
1
No encuentro que esto haga que el código sea mucho más difícil de leer. (En serio, si alguien tiene problemas con un bucle tan simple, debería estar preocupado). Pero: 1) Me sorprendería terriblemente si después de 4 décadas de lenguajes descendientes en C y C, todos los compiladores serios ya no hicieran esto. para ti; y 2) esto puede no ser un punto caliente. No parece que valga la pena que alguien pase 5 segundos adicionales analizándolo a menos que esté dentro de una biblioteca (por ejemplo, la implementación de una estructura de datos).
Doval
1
En C # / .NET, la variante que usa npuede ser más lenta que la que usa, array.Lengthya que el JITter podría no notar que podría eliminar las verificaciones de los límites de la matriz.
CodesInChaos

Respuestas:

27

La variable ngarantiza que el código generado no recupere la longitud de la matriz para cada iteración.

Es una optimización que puede marcar la diferencia en el tiempo de ejecución dependiendo del idioma utilizado, si la matriz es en realidad un objeto de colección o una "matriz" de JavaScript, y otros detalles de optimización.

Jerry101
fuente
3
No lo hace Si la longitud de la matriz se obtiene cada iteración depende no solo del lenguaje, sino también del compilador y las opciones del compilador y de lo que se hace en el bucle (estoy hablando de C y C ++)
BЈовић
.. Y aun así, personalmente pondría la asignación n justo por encima del ciclo si realmente lo quisiera, ya que, como señaló OP, esta es una construcción rara (YMMV) para usar y es fácilmente perdida / no entendida por otros desarrolladores que leen el código.
cwap
20
Tal como está escrito, se extiende nal bucle, lo que suele ser algo bueno. Lo definiría antes del ciclo solo si quisiera usarlo nuevamente más tarde.
Jerry101
44
@ Jerry101: Aparentemente, el fragmento de ejemplo es JavaScript, donde no existe un alcance de bucle ...
Bergi
1
@ BЈовић: en la práctica, el compilador rara vez logra demostrar que el tamaño de la matriz no varía (por lo general, es trivialmente demostrable solo si el vector sobre el que realiza el bucle es local para la función y solo está llamando a funciones en línea en el bucle).
Matteo Italia
2

La obtención de la longitud de la matriz puede ser fácilmente "más costosa" que la acción real sobre la que se repite.

Entonces, si el conjunto no cambia, solo consulte la longitud una vez.

No con matrices, sino con conjuntos de registros provenientes de un servidor sql. He visto mejoras dramáticas al no consultar el recuento de registros en cada iteración. (por supuesto, haga esto solo si puede garantizar que la matriz o el conjunto de registros no se modifique durante este proceso).

Pieter B
fuente
Si el recuento de registros cambia, entonces, ¿qué? Digamos que había 100 artículos, y mientras procesa el artículo 50, se inserta un artículo después del # 25. Entonces, cuando procesa el artículo 51, es el mismo que el 50 que ya procesó, y las cosas salen mal. Entonces, el problema no es que la longitud cambie, es mucho más penetrante.
gnasher729
@ gnasher729 una vez que entras en un comportamiento asimétrico, hay muchas cosas que pueden salir mal. Pero hay cosas que puede hacer en contra de eso, como iniciar y la transacción SQL.
Pieter B
2

Se me ocurre la idea de que la longitud de la "matriz" puede cambiar durante la ejecución del bucle for, pero no queremos hacer un bucle más allá del tamaño original, pero no puedo imaginar tal escenario.

Las personas que hablan sobre el rendimiento probablemente estén en lo cierto porque esta es la razón por la que se escribió de esa manera (la persona que lo escribió de esa manera podría no ser correcta porque afecta significativamente el rendimiento, pero ese es otro asunto).

Sin embargo, para responder a esta parte de su pregunta, creo que es más probable que suceda cuando el propósito principal del bucle es modificar la matriz sobre la que se repite. Considere algo como esto, en pseudocódigo:

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

Por supuesto que hay otras formas de escribirlo. En este caso, podría haber verificado los más al mismo tiempo que verificaba si los destinatarios aceptaban sus invitaciones y produje la lista completa de invitados una invitación a la vez. No necesariamente quiero combinar esas dos operaciones, pero no puedo pensar de inmediato en una razón por la que eso es definitivamente incorrecto y, por lo tanto, se requiere este diseño . Es una opción a considerar, ocasionalmente.

En realidad, creo que lo que está más mal con este código es que la pertenencia a la guestsmatriz significa cosas diferentes en diferentes puntos del código. Por lo tanto, ciertamente no lo llamaría un "patrón", pero no sé si es un defecto suficiente para descartar hacer algo de este tipo :-)

Steve Jessop
fuente
La matriz también podría ser modificada por otro hilo. Esto evita que el compilador optimice la recuperación de longitud. Entonces, en cuestión, el programador dice explícitamente que la matriz no cambia incluso en otros hilos.
Basilevs
0

Si es posible, debe usar un iterador que atraviese todos los elementos de la matriz. Utiliza la matriz solo como una secuencia, no como almacenamiento de acceso aleatorio, por lo que sería obvio que un iterador que solo hace un acceso secuencial sería más rápido. Imagina que una matriz es en realidad un árbol binario, pero alguien ha escrito un código que determina la "longitud" contando los elementos y el código que accede al elemento i-ésimo, también atravesando el elemento, cada uno en O (n ) Un iterador puede ser muy rápido, su ciclo será muy lento.

gnasher729
fuente