'<' versus '! =' como condición en un ciclo 'for'?

31

Digamos que tiene el siguiente forbucle *:

for (int i = 0; i < 10; ++i) {
    // ...
}

que comúnmente también podría escribirse como:

for (int i = 0; i != 10; ++i) {
    // ...
}

Los resultados finales son los mismos, entonces, ¿hay algún argumento real para usar uno sobre el otro? Personalmente uso el primero en caso de que ipor alguna razón se vuelva loco y se salte el valor 10.

* Disculpe el uso de números mágicos, pero es solo un ejemplo.

gablin
fuente
18
La solución superior a cualquiera de los que es usar el operador de flecha:int i = 10; while(i --> 0) { /* stuff */ }
corsiKa
@glowcoder el operador de flecha es mi favorito
Carson Myers
3
@ Glowcoder, agradable, pero atraviesa desde la parte posterior. Puede que no siempre quieras eso.
2
@SuperElectric, analiza comowhile ((i--) > 0)
Kristopher Johnson
17
Hay una historia (probablemente apócrifa) sobre un accidente industrial causado por una prueba de bucle while para una entrada de sensor que es = MAX_TEMP. Desafortunadamente, un día la entrada del sensor pasó de ser menor que MAX_TEMP a mayor que MAX_TEMP sin pasar por MAX_TEMP. El proceso se sobrecalentó sin ser detectado y se produjo un incendio. A veces hay una diferencia entre! = Y <.
Charles E. Grant

Respuestas:

59

La razón para elegir uno u otro es debido a la intención y, como resultado de esto, aumenta la legibilidad .

Intención: el ciclo debe ejecutarse mientras isea ​​menor que 10, no mientras ino sea igual a 10. Aunque este último puede ser el mismo en este caso particular, no es lo que quiere decir, por lo que no debería ser escrito así

Legibilidad: el resultado de escribir lo que quiere decir es que también es más fácil de entender. Por ejemplo, si usa i != 10, alguien que lee el código puede preguntarse si dentro del bucle ipodría haber más de 10 y si el bucle debería continuar (por cierto: es un mal estilo meterse con el iterador en otro lugar que no sea en la cabeza de la fordeclaración, pero eso no significa que la gente no lo haga y, como resultado, los mantenedores lo esperan).

Deckard
fuente
3
No, el bucle no debe ejecutarse mientras isea ​​"menor que 10", debe ejecutarse siempre que no se alcance el límite superior (en este caso). Cuando se utiliza la lógica del intervalo C ++ / rango del iterador, es mucho más lógico expresar esto a través de !=que <.
Konrad Rudolph el
14
@ Konrad No estoy en desacuerdo con eso en absoluto. El punto principal no es ser dogmático, sino elegir el constructo que mejor exprese la intención de la condición. Entonces, si eliges recorrer algo que comienza en 0 y se mueve hacia arriba, entonces en <realidad lo expresa con precisión.
Deckard el
66
@ Konrad, te estás perdiendo el punto. El operador de incremento en este bucle hace obvio que la condición del bucle es un límite superior, no una comparación de identidad. Si estuvieras decrementando, sería un límite inferior. Si está iterando sobre una colección no ordenada, la identidad podría ser la condición correcta.
Alex Feinman
3
@ Alex, el incremento no era mi punto. Un intervalo ni siquiera necesariamente tiene un orden. Simplemente se repite ... bueno, algo hasta que llega al final. Por ejemplo, elementos en un conjunto hash. En C ++, esto se hace usando iteradores y un forbucle. Usar <aquí sería una tontería. Además, Deckard parece reconocer eso. Estoy muy de acuerdo con su comentario en respuesta al mío.
Konrad Rudolph
1
Tenga en cuenta que si usa un búfer rotativo con punteros de persecución, DEBE usar!=
SF.
48

El <patrón generalmente se puede usar incluso si el incremento no es exactamente 1.

Esto permite una única forma común de hacer bucles independientemente de cómo se haga realmente.


fuente
66
También permite imodificar dentro del bucle de forma totalmente segura si es necesario.
Matthew Scharley el
1
o si 'i' se modifica de forma totalmente insegura ... Otro equipo tuvo un problema extraño en el servidor. Seguía informando el uso del 100% de la CPU y debe ser un problema con el servidor o el sistema de monitoreo, ¿verdad? No, encontré una condición de bucle escrita por un 'programador senior experto' con el mismo problema del que estamos hablando.
jqa
12
Excepto que no todos los bucles de C ++ pueden usar <, como los iteradores sobre una secuencia, por !=lo que sería una forma común de hacer bucles en C ++.
David Thornley
@SnOrfus: no estoy analizando ese comentario. En general, no obtendrá excepciones confiables para incrementar demasiado un iterador (aunque hay situaciones más específicas en las que lo hará).
David Thornley
Bueno, sigo el <patrón porque requiere una pulsación de tecla en lugar de dos con el >patrón y cuatro con !=. Es una cuestión de eficacia similar a OCD realmente.
Spoike
21

En C ++, la recomendación de Scott Myers en C ++ más eficaz (elemento 6) es usar siempre el segundo, a menos que tenga una razón para no hacerlo, ya que significa que tiene la misma sintaxis para los índices de iterador e entero para que pueda cambiar sin problemas entre un intiterador sin ningún cambio en la sintaxis.

En otros idiomas esto no se aplica, así que supongo <que probablemente sea preferible debido al punto de Thorbjørn Ravn Andersen.

Por cierto, el otro día estaba discutiendo esto con otro desarrollador y dijo que la razón para preferir <más !=se debe a que iaccidentalmente podría incrementar en más de uno, y que podría causar la condición de ruptura no se cumplirá; eso es IMO una carga de tonterías.


fuente
17
"carga de tonterías" ... hasta el día en que accidentalmente tenga un i ++ adicional en el cuerpo del bucle.
2
+1, especialmente para "carga de tonterías", porque lo es. Si el cuerpo del bucle aumenta accidentalmente el contador, tiene problemas mucho mayores.
Konrad Rudolph el
12
@B Tyler, solo somos humanos, y han ocurrido errores más grandes antes. Considere <tener una programación más defensiva que !=entonces ...
2
@ Thorbjørn Ravn Andersen - No estoy diciendo que no estoy de acuerdo contigo, sí; <es más defensivo y mis colegas odian si escribo !=y no lo hago. Sin embargo, hay un caso para !=C ++ y eso es lo que he presentado.
99
Un escenario en el que uno puede terminar con un extra accidental i++es cuando se cambia un ciclo while en un ciclo for. Sin embargo, no estoy seguro de que tener la mitad del número de iteraciones sea mejor que un bucle (casi) infinito; esto último probablemente haría que el error sea más obvio.
ak2
12

Usar (i <10) es, en mi opinión, una práctica más segura. Captura el número máximo de casos potenciales para dejar de fumar: todo lo que es mayor o igual que 10. Contraste esto con el otro caso (i! = 10); solo detecta un posible caso de abandono, cuando tengo exactamente 10.

Un subproducto de esto es que mejora la legibilidad. Además, si el incremento es otro 1, puede ayudar a minimizar la probabilidad de un problema si cometemos un error al escribir el caso para dejar de fumar.

Considerar:

1) for (i = 1; i < 14; i+=2)

2) for (i = 1; i != 14; i+=2)

Aunque es probable que ambos casos sean defectuosos / incorrectos, es probable que el segundo sea MÁS incorrecto ya que no se cerrará. El primer caso se cerrará, y existe una mayor probabilidad de que se cierre en el lugar correcto, aunque 14 es probablemente el número incorrecto (15 probablemente sería mejor).

Chispeante
fuente
¡El primer caso puede ser correcto! Si desea iterar sobre todos los números naturales menores que 14, entonces no hay mejor manera de expresarlo: calcular el límite superior "adecuado" (13) sería simplemente estúpido.
maaartinus
9

Aquí hay otra respuesta que nadie parece haber llegado todavía. forLos bucles deben usarse cuando necesita iterar sobre una secuencia . El uso !=es el método más conciso para establecer la condición de terminación del bucle. Sin embargo, el uso de un operador menos restrictivo es un lenguaje de programación defensiva muy común. Para los enteros no importa: es solo una elección personal sin un ejemplo más específico. Recorrer colecciones con iteradores que desea usar !=por las razones que otros han declarado. Si considera secuencias de floato double, entonces desea evitar !=a toda costa.

Lo que quería señalar es que forse usa cuando necesita iterar sobre una secuencia. La secuencia generada tiene un punto de inicio, un intervalo y una condición de finalización. Estos se especifican de manera concisa dentro de la fordeclaración. Si se encuentra (1) sin incluir la parte del paso delfor (2) especificando algo truecomo la condición de protección, entonces no debería estar usando un forbucle.

El whilebucle se utiliza para continuar procesando mientras se cumple una condición específica. Si no está procesando una secuencia, entonces probablemente desee un whilebucle. Los argumentos de la condición de guardia son similares aquí, pero la decisión entre a whiley un forbucle debe ser muy consciente. El whilebucle se subestima en los círculos de C ++ IMO.

Si está procesando una colección de elementos (un foruso de bucle muy común ), entonces realmente debería usar un método más especializado. Desafortunadamente, std::for_eaches bastante doloroso en C ++ por varias razones. En muchos casos, la separación del cuerpo de un forbucle en una función independiente (aunque algo dolorosa) da como resultado una solución mucho más limpia. Cuando trabaje con colecciones, considere std::for_each,std::transform o std::accumulate. La implementación de muchos algoritmos se vuelve concisa y clara como el cristal cuando se expresa de esta manera. Sin mencionar que aislar el cuerpo del bucle en una función / método separado lo obliga a concentrarse en el algoritmo, sus requisitos de entrada y resultados.

Si está utilizando Java, Python, Ruby o incluso C ++ 0x , entonces debería estar utilizando una colección foreach loop adecuada . A medida que los compiladores de C ++ implementen esta característica, fordesaparecerán varios bucles y este tipo de discusiones.

D.Shawley
fuente
2
"Sin embargo, usar un operador menos restrictivo es un lenguaje de programación defensiva muy común". Esto lo resume más o menos.
James P.
+1 para discutir las diferencias de intenciones con la comparación con while, fory foreach(o equivalente de idioma)
Kevin Peno
Estrictamente hablando, el whilebucle se usa para continuar procesando hasta que no se cumpla una condición específica
Alnitak
@Alnitak - D'oh ... lo arreglaré :)
D.Shawley
Estaba confundido por los dos posibles significados de "menos restrictivo": podría referirse al operador indulgente en los valores que pasa ( <) o al operador indulgente en los valores que falla ( !=).
Mateen Ulhaq
4

Usar "menor que" es (generalmente) semánticamente correcto, realmente quiere decir contar hasta ique ya no sea menor que 10, por lo que "menor que" transmite sus intenciones claramente. El uso de "no igual" obviamente funciona prácticamente en casos de llamada, pero transmite un significado ligeramente diferente. En algunos casos, esto puede ser lo que necesita, pero en mi experiencia este nunca ha sido el caso. Si realmente tuvo un caso en el que ipodría ser más o menos de 10 pero desea seguir haciendo bucles hasta que sea igual a 10, entonces ese código realmente necesitaría comentarios muy claramente, y probablemente podría escribirse mejor con alguna otra construcción, como como un whilebucle quizás.

Steve
fuente
2

Una razón por la que preferiría un less thansobre a not equalses actuar como guardia. En algunas circunstancias limitadas (mala programación o desinfección) elnot equals podría omitir mientras less thantodavía estaría en vigencia.

Aquí hay un ejemplo donde la falta de una verificación de desinfección ha dado resultados extraños: http://www.michaeleisen.org/blog/?p=358

James P.
fuente
0

Aquí es una de las razones por las que podría preferir utilizar <en lugar de !=. Si está utilizando un lenguaje que tiene un alcance de variable global, ¿qué sucede si se modifica otro código i? Si está utilizando en <lugar de !=, lo peor que sucede es que la iteración termina más rápido: tal vez algunos otros incrementos de código ipor accidente, y omita algunas iteraciones en el bucle for. Pero, ¿qué sucede si realiza un ciclo de 0 a 10, y el ciclo llega a 9, y algunos subprocesos mal escritos aumentan ipor alguna extraña razón? Entonces su ciclo termina esa iteración e incrementosi modo que el valor ahora sea 11. Como el ciclo ha omitido la condición de salida ( inunca igual a 10), ahora se repetirá infinitamente.

No necesariamente tiene que ser una lógica de tipo de subprocesos y variables globales particularmente extraña lo que causa esto. Puede ser que esté escribiendo un bucle que necesita retroceder. Si está mutando identro del bucle y arruina su lógica, !=es menos probable que tenga un límite superior en lugar de un a lo que lo dejará en un bucle infinito.

Tom Morris
fuente
3
Por supuesto, esto parece un argumento perfecto para los bucles for-each y un estilo de programación más funcional en general. Pero, ¿por qué querrías hacer eso cuando las variables mutables son mucho más divertidas ?
Tom Morris
3
No creo que sea una muy buena razón. De cualquier manera, tiene un error que necesita ser encontrado y reparado, pero un bucle infinito tiende a hacer que un error sea bastante obvio.
ak2
0

En el mundo embebido, especialmente en entornos ruidosos, no puede contar con que la RAM necesariamente se comporte como debería. En un condicional (para, mientras, si) donde compara usando '==' o '! =' Siempre corre el riesgo de que sus variables omitan ese valor crucial que termina el ciclo - esto puede tener consecuencias desastrosas - Mars Lander Nivel de consecuencias. El uso de '<' o '>' en la condición proporciona un nivel adicional de seguridad para atrapar las 'incógnitas desconocidas'.

En el ejemplo original, si ise catapultara inexplicablemente a un valor mucho mayor que 10, la comparación '<' detectaría el error de inmediato y saldría del bucle, pero '! =' Continuaría contando hasta que se ienvuelva más allá de 0 y viceversa. a 10.

oosterwal
fuente
2
Existe otra variación relacionada con código como for (i=4; i<length; i++) csum+=dat[i];donde los paquetes legítimos siempre tendrían lengthal menos cuatro, pero uno podría recibir paquetes corruptos. Si los diferentes tipos de paquetes tienen diferentes requisitos de longitud, es posible que desee validar la longitud como parte del procesamiento que es diferente para cada tipo de paquete, pero primero valide la suma de verificación como parte de la lógica común. Si se recibe un paquete de menor longitud, en realidad no importa si el cálculo de la suma de verificación informa "bueno" o "malo", pero no debería bloquearse.
supercat