performance - sentencia - estructura while
¿Debería uno usar<o<= en un bucle for (30)
Si tuviera que iterar 7 veces a través de un bucle, ¿usaría:
for (int i = 0; i < 7; i++)
o:
for (int i = 0; i <= 6; i++)
Hay dos consideraciones:
- actuación
- legibilidad
Para el rendimiento estoy asumiendo Java o C #. ¿Importa si se usa "menor que" o "menor que o igual a"? Si tiene conocimientos para un idioma diferente, indique cuál.
Para la legibilidad estoy asumiendo matrices basadas en 0.
UPD: Mi mención de arreglos basados en 0 puede haber confundido las cosas. No estoy hablando de iterar a través de elementos de matriz. Solo un ciclo general.
Hay un buen punto a continuación sobre el uso de una constante a la que explicaría qué es este número mágico. Entonces, si tuviera " int NUMBER_OF_THINGS = 7
", entonces " i <= NUMBER_OF_THINGS - 1
" se vería raro, ¿verdad?
@Chris, Tu afirmación acerca de .Length es costoso en .NET es realmente falso y en el caso de los tipos simples es exactamente lo contrario.
int len = somearray.Length;
for(i = 0; i < len; i++)
{
somearray[i].something();
}
es en realidad más lento que
for(i = 0; i < somearray.Length; i++)
{
somearray[i].something();
}
El último es un caso optimizado por el tiempo de ejecución. Como el tiempo de ejecución puede garantizar que es un índice válido en la matriz, no se realizan comprobaciones de límites. En el primero, el tiempo de ejecución no puede garantizar que no haya sido modificado antes del ciclo y obliga a verificar los límites del conjunto para cada búsqueda de índice.
Ambos bucles iteran 7 veces. Yo diría que el que tiene un 7 es más legible / claro, a menos que tenga una muy buena razón para el otro.
Como la gente ha observado, no hay diferencia en ninguna de las dos alternativas que mencionaste. Solo para confirmar esto, hice algunos benchmarking simples en JavaScript.
Puedes ver los resultados here . Lo que no está claro de esto es que si cambio la posición de la primera y segunda pruebas, los resultados de esas dos pruebas cambian, esto es claramente un problema de memoria. Sin embargo, la tercera prueba, una donde invierto el orden de la iteración, es claramente más rápida.
Como todo el mundo dice, es costumbre usar iteradores indexados en 0 incluso para cosas que están fuera de las matrices. Si todo comienza en 0
y termina en n-1
, y los límites inferiores siempre son <=
y los límites superiores siempre son <
, hay mucho menos que pensar al revisar el código.
Como un pequeño aparte, al recorrer una matriz u otra colección en .Net, encuentro
foreach (string item in myarray)
{
System.Console.WriteLine(item);
}
ser más legible que el bucle numérico for. Por supuesto, esto supone que el contador actual Int no se usa en el código de bucle. No sé si hay un cambio de rendimiento.
Creo que cualquiera de los dos está bien, pero cuando lo elijas, sigue uno u otro. Si está acostumbrado a usar <=, intente no usar <y viceversa.
Prefiero <=, pero en situaciones en las que trabajas con índices que comienzan en cero, probablemente intente usar <. Sin embargo, es toda una preferencia personal.
De regreso en la universidad, recuerdo algo acerca de que estas dos operaciones son similares en tiempo de cómputo en la CPU. Por supuesto, estamos hablando a nivel de montaje.
Sin embargo, si habla C # o Java, realmente no creo que uno sea un impulso de velocidad sobre el otro. Es probable que los pocos nanosegundos que gane no valgan la confusión que introduzca.
Personalmente, autorizaría el código que tiene sentido desde el punto de vista de la implementación comercial, y me aseguraré de que sea fácil de leer.
Edsger Dijkstra escribió un artículo sobre este tema en 1982, donde defiende una <= i <upper superior:
Hay un número natural más pequeño. La exclusión del límite inferior -como en b) yd) - fuerza una subsecuencia que comienza en el número natural más pequeño, el límite inferior como se menciona en el reino de los números antinaturales. Eso es feo, por lo que para el límite inferior preferimos el ≤ como en a) yc). Considere ahora las subsecuencias que comienzan en el número natural más pequeño: la inclusión del límite superior obligaría a este último a ser antinatural en el momento en que la secuencia se redujo a la vacía. Eso es feo, por lo que para el límite superior preferimos <como en a) yd). Concluimos que la convención a) es preferible.
El primero es más idiomatic . En particular, indica (en un sentido basado en 0) el número de iteraciones. Cuando utilizo algo basado en 1 (por ejemplo, JDBC, IIRC), podría sentir la tentación de usar <=. Asi que:
for (int i=0; i < count; i++) // For 0-based APIs
for (int i=1; i <= count; i++) // For 1-based APIs
Esperaría que la diferencia de rendimiento sea insignificantemente pequeña en el código del mundo real.
En C ++, prefiero utilizar !=
, Que se puede usar con todos los contenedores STL. No todos los iteradores de contenedor STL son menos que comparables.
En Java 1.5 puedes hacer
for (int i: myArray) {
...
}
entonces para el caso de la matriz no necesita preocuparse.
Esto cae directamente bajo la categoría de "Hacer que el código incorrecto se vea mal" .
En los lenguajes de indexación basados en cero, como Java o C #, las personas están acostumbradas a las variaciones en la condición index < count
. Por lo tanto, aprovechar esta convención de facto haría que los errores uno por uno sean más obvios.
En cuanto al rendimiento: cualquier buen compilador que merezca la pena su huella de memoria debería mostrarse como un no problema.
Estoy de acuerdo con la gente diciendo que el 7 tiene sentido en este caso, pero agregaría que en el caso en que el 6 es importante, digamos que quiere dejar en claro que solo está actuando en objetos hasta el sexto índice, entonces el <= es mejor, ya que hace que el 6 sea más fácil de ver.
Estrictamente desde un punto de vista lógico, debes pensar que < count
sería más eficiente que <= count
por la razón exacta por la que <=
probará la igualdad.
Gran pregunta Mi respuesta: use el tipo A (''<'')
- Usted ve claramente cuántas repeticiones tiene (7).
- La diferencia entre dos puntos finales es el ancho del rango
- Menos caracteres lo hace más legible
- Con más frecuencia tiene la cantidad total de elementos
i < strlen(s)
lugar del índice del último elemento, por lo que la uniformidad es importante.
Otro problema es con todo este constructo. aparece 3 veces en ella, por lo que puede ser mal escrita. La construcción for-loop dice cómo hacer en lugar de qué hacer . Sugiero adoptar esto:
BOOST_FOREACH(i, IntegerInterval(0,7))
Esto es más claro, compila exactamente las mismas instrucciones de asm, etc. Pregúntame por el código de IntegerInterval si quieres.
Hacer el hábito de usar <lo hará coherente tanto para usted como para el lector cuando esté iterando a través de una matriz. Será más simple para todos tener una convención estándar. Y si está utilizando un lenguaje con matrices basadas en 0, entonces <es la convención.
Esto casi seguramente importa más que cualquier diferencia de rendimiento entre <y <=. Primero busca la funcionalidad y la legibilidad, luego optimiza.
Otra nota es que sería mejor tener el hábito de hacer ++ i en vez de i ++, ya que fetch and increment requiere un incremento temporal y fetch no lo hace. En el caso de los enteros, es probable que el compilador optimice el valor temporal, pero si el tipo de iteración es más complejo, es posible que no sea capaz de hacerlo.
Hay muchas buenas razones para escribir i <7. Tener el número 7 en un bucle que itera 7 veces es bueno. El rendimiento es efectivamente idéntico. Casi todos escriben i <7. Si está escribiendo para leer, use el formulario que todos reconocerán al instante.
Los operadores ''<'' y ''<='' tienen exactamente el mismo costo de rendimiento.
El operador ''<'' es un estándar y más fácil de leer en un bucle basado en cero.
Usar ++ i en lugar de i ++ mejora el rendimiento en C ++, pero no en C # - No sé sobre Java.
No creo que haya una diferencia de rendimiento. La segunda forma es definitivamente más legible, sin embargo, no tienes que restar mentalmente una para encontrar el último número de iteración.
EDITAR: veo a otros en desacuerdo. Personalmente, me gusta ver los números de índice reales en la estructura de bucle. Tal vez sea porque recuerda más a la sintaxis de Perl 0..6
, que sé que es equivalente a (0,1,2,3,4,5,6)
. Si veo un 7, debo consultar al operador que está al lado para ver que, de hecho, el índice 7 nunca se alcanza.
No hace una diferencia efectiva cuando se trata de rendimiento. Por lo tanto, usaría lo que sea más fácil de entender en el contexto del problema que está resolviendo.
No uses números mágicos.
¿Por qué es 7? (o 6 para ese asunto).
usa el símbolo correcto para el número que quieres usar ...
En cuyo caso, creo que es mejor usar
for ( int i = 0; i < array.size(); i++ )
Primero, no use 6 o 7.
Mejor usar:
int numberOfDays = 7;
for (int day = 0; day < numberOfDays ; day++){
}
En este caso, es mejor que usar
for (int day = 0; day <= numberOfDays - 1; day++){
}
Aún mejor (Java / C #):
for(int day = 0; day < dayArray.Length; i++){
}
Y aún mejor (C #)
foreach (int day in days){// day : days in Java
}
El ciclo inverso es de hecho más rápido, pero como es más difícil de leer (si no lo hacen otros programadores), es mejor evitarlo. Especialmente en C #, Java ...
Recuerdo que desde mis días cuando hicimos la Asamblea 8086 en la universidad, fue más productivo:
for (int i = 6; i > -1; i--)
ya que hubo una operación JNS que significa Saltar si no hay señal. Usar esto significaba que no había búsqueda de memoria después de cada ciclo para obtener el valor de comparación y tampoco comparar. En la actualidad, la mayoría de los compiladores optimizan el uso del registro, por lo que la cuestión de la memoria ya no es importante, pero aún así se obtiene una comparación no requerida.
Por cierto, poner 7 o 6 en tu bucle es introducir un " número mágico ". Para una mejor legibilidad, debe usar una constante con un Nombre Revelador de Intención. Me gusta esto:
const int NUMBER_OF_CARS = 7;
for (int i = 0; i < NUMBER_OF_CARS; i++)
EDITAR: Las personas no están recibiendo lo del montaje, por lo que obviamente se requiere un ejemplo más completo:
Si hacemos para (i = 0; i <= 10; i ++) necesita hacer esto:
mov esi, 0
loopStartLabel:
; Do some stuff
inc esi
; Note cmp command on next line
cmp esi, 10
jle exitLoopLabel
jmp loopStartLabel
exitLoopLabel:
Si lo hacemos por (int i = 10; i> -1; i--), entonces puede salirse con la suya con esto:
mov esi, 10
loopStartLabel:
; Do some stuff
dec esi
; Note no cmp command on next line
jns exitLoopLabel
jmp loopStartLabel
exitLoopLabel:
Acabo de comprobar y el compilador de C ++ de Microsoft no hace esta optimización, pero lo hace si lo hace:
for (int i = 10; i >= 0; i--)
Entonces, la moraleja es que si está usando Microsoft C ++ †, y ascendente o descendente no hace diferencia, para obtener un ciclo rápido, debe usar:
for (int i = 10; i >= 0; i--)
en lugar de cualquiera de estos:
for (int i = 10; i > -1; i--)
for (int i = 0; i <= 10; i++)
Pero, francamente, obtener la legibilidad de "for (int i = 0; i <= 10; i ++)" normalmente es mucho más importante que omitir un comando del procesador.
† Otros compiladores pueden hacer cosas diferentes.
Siempre he preferido:
for ( int count = 7 ; count > 0 ; -- count )
Siempre uso <array.length porque es más fácil de leer que <= array.length-1.
También tiene <7 y dado que sabe que está comenzando con un índice 0, debería ser intuitivo que el número sea el número de iteraciones.
También podría usar !=
lugar. De esta forma, obtendrás un bucle infinito si cometes un error en la inicialización, lo que hace que el error se note antes y que los problemas que causa se limiten a quedarse atascado en el bucle (en lugar de tener un problema mucho más tarde y no encontrarlo). eso).
Tantas respuestas ... pero creo que tengo algo que agregar.
Mi preferencia es que los números literales muestren claramente qué valores "yo" tomaré en el ciclo . Entonces en el caso de iterar a través de una matriz basada en cero:
for (int i = 0; i <= array.Length - 1; ++i)
Y si solo está iterando, sin iterar a través de una matriz, contar del 1 al 7 es bastante intuitivo:
for (int i = 1; i <= 7; ++i)
La legibilidad supera el rendimiento hasta que lo perfila, ya que probablemente no sepa qué hará el compilador o el tiempo de ejecución con su código hasta entonces.
Visto desde un punto de vista optimizador, no importa.
Visto desde un punto de vista de estilo de código, prefiero <. Razón:
for ( int i = 0; i < array.size(); i++ )
es mucho más legible que
for ( int i = 0; i <= array.size() -1; i++ )
también <le da el número de iteraciones de inmediato.
Otro voto para <es que puede evitar muchos errores accidentales de uno en uno.
Yo diría que use la versión "<7" porque eso es lo que leerá la mayoría de la gente, de modo que si las personas leen su código con poca atención, podrían interpretarlo incorrectamente.
No me preocuparía si "<" es más rápido que "<=", simplemente busque legibilidad.
Si desea aumentar la velocidad, considere lo siguiente:
for (int i = 0; i < this->GetCount(); i++)
{
// Do something
}
Para aumentar el rendimiento, puede reorganizarlo ligeramente para:
const int count = this->GetCount();
for (int i = 0; i < count; ++i)
{
// Do something
}
Observe la eliminación de GetCount () del bucle (porque se consultará en cada bucle) y el cambio de "i ++" a "++ i".
Yo prefiero:
for (int i = 0; i < 7; i++)
Creo que eso se traduce más fácilmente en "iterar a través de un ciclo 7 veces".
No estoy seguro de las implicaciones de rendimiento. Sospecho que las diferencias se recopilarán.