Esta es la forma más popular (me parece) de verificar si un valor está en una matriz:
for (int x : array)
{
if (x == value)
return true;
}
return false;
Sin embargo, en un libro que leí hace muchos años por, probablemente, Wirth o Dijkstra, se dijo que este estilo es mejor (en comparación con un bucle while con una salida adentro):
int i = 0;
while (i < array.length && array[i] != value)
i++;
return i < array.length;
De esta manera, la condición de salida adicional se convierte en una parte explícita del bucle invariante, no hay condiciones ocultas y salidas dentro del bucle, todo es más obvio y más en una forma de programación estructurada. En general, preferí este último patrón siempre que era posible y usé el for
bucle para iterar solo de a
a b
.
Y, sin embargo, no puedo decir que la primera versión sea menos clara. Quizás sea aún más claro y fácil de entender, al menos para los principiantes. ¿Entonces todavía me pregunto cuál es mejor?
¿Tal vez alguien puede dar una buena razón a favor de uno de los métodos?
Actualización: no se trata de puntos de retorno de funciones múltiples, lambdas o de encontrar un elemento en una matriz per se. Se trata de cómo escribir bucles con invariantes más complejos que una sola desigualdad.
Actualización: OK, veo el punto de las personas que responden y comentan: mezclé el bucle foreach aquí, que en sí mismo es mucho más claro y legible que un bucle while. No debería haber hecho eso. Pero esta también es una pregunta interesante, así que vamos a dejarlo como está: foreach-loop y una condición adicional en el interior, o while-loop con un ciclo explícito invariante y una condición posterior después. Parece que el foreach-loop con una condición y una salida / break está ganando. Crearé una pregunta adicional sin el foreach-loop (para una lista vinculada).
fuente
collection.contains(foo)
Respuestas:
Creo que para bucles simples, como estos, la primera sintaxis estándar es mucho más clara. Algunas personas consideran confusas las devoluciones múltiples o un olor a código, pero para un fragmento de código tan pequeño, no creo que este sea un problema real.
Se vuelve un poco más discutible para bucles más complejos. Si el contenido del bucle no cabe en su pantalla y tiene varios retornos en el bucle, se debe argumentar que los múltiples puntos de salida pueden hacer que el código sea más difícil de mantener. Por ejemplo, si tuviera que asegurarse de que se ejecutara algún método de mantenimiento de estado antes de salir de la función, sería fácil no agregarlo a una de las declaraciones de retorno y causaría un error. Si todas las condiciones finales se pueden verificar en un ciclo while, solo tiene un punto de salida y puede agregar este código después.
Dicho esto, especialmente con los bucles, es bueno intentar poner tanta lógica como sea posible en métodos separados. Esto evita muchos casos en los que el segundo método tendría ventajas. Los bucles Lean con lógica claramente separada serán más importantes que los estilos que use. Además, si la mayoría de la base de código de su aplicación está usando un estilo, debe seguir con ese estilo.
fuente
Esto es facil.
Casi nada importa más que la claridad para el lector. La primera variante la encontré increíblemente simple y clara.
La segunda versión 'mejorada', tuve que leer varias veces y asegurarme de que todas las condiciones de borde fueran correctas.
Hay CERO DUDA, que es un mejor estilo de codificación (el primero es mucho mejor).
Ahora, lo que está CLARO para las personas puede variar de persona a persona. No estoy seguro de que haya estándares objetivos para eso (aunque publicar en un foro como este y obtener una variedad de aportes de la gente puede ayudar).
En este caso particular, sin embargo, puedo decirle por qué el primer algoritmo es más claro: sé cómo se ve y hace la iteración de C ++ sobre una sintaxis de contenedor. Lo he internalizado. Alguien UNFAMILIAR (su nueva sintaxis) con esa sintaxis podría preferir la segunda variación.
Pero una vez que conoce y entiende esa nueva sintaxis, es un concepto básico que puede usar. Con el enfoque de iteración de bucle (segundo), debe verificar cuidadosamente que el usuario verifique CORRECTAMENTE todas las condiciones de borde para recorrer toda la matriz (por ejemplo, menos que en lugar de menor o igual, el mismo índice utilizado para la prueba y para indexar, etc.)
fuente
longerLength = true
, entoncesreturn longerLength
.length
. Si en realidad se declarara como una matriz y no como un puntero, podrían usarsizeof
, o si fuera unastd::array
, la función de miembro correcta es quesize()
no hay unalength
propiedad.sizeof
estaría en bytes ... El más genérico desde C ++ 17 esstd::size()
.No exactamente. La variable
i
existe fuera del bucle while aquí y, por lo tanto, es parte del alcance externo, mientras que (juego de palabras intencionado)x
delfor
bucle -lo existe solo dentro del alcance del bucle. El alcance es una forma muy importante de introducir estructura a la programación.fuente
Los dos bucles tienen una semántica diferente:
El primer bucle simplemente responde una simple pregunta de sí / no: "¿La matriz contiene el objeto que estoy buscando?" Lo hace de la manera más breve posible.
El segundo bucle responde a la pregunta: "Si la matriz contiene el objeto que estoy buscando, ¿cuál es el índice de la primera coincidencia?" Nuevamente, lo hace de la manera más breve posible.
Dado que la respuesta a la segunda pregunta proporciona estrictamente más información que la respuesta a la primera, puede elegir responder la segunda pregunta y luego derivar la respuesta de la primera pregunta. Eso es lo que hace la línea
return i < array.length;
, de todos modos.Creo que generalmente es mejor usar la herramienta que se ajuste al propósito a menos que pueda reutilizar una herramienta ya existente y más flexible. Es decir:
bool
variable y romper también está bien. (Evita la segundareturn
declaración, la respuesta está disponible en una variable en lugar de un retorno de función).std::find
está bien (¡reutilización de código!).bool
no lo es.fuente
Sugeriré una tercera opción por completo:
Hay muchas razones diferentes para iterar sobre una matriz: verifique si existe un valor específico, transforme la matriz en otra matriz, calcule un valor agregado, filtre algunos valores fuera de la matriz ... Si usa un bucle plano simple, no está claro de un vistazo específicamente cómo se está utilizando el bucle for. Sin embargo, la mayoría de los lenguajes modernos tienen API ricas en sus estructuras de datos de matriz que hacen que estos diferentes intentos sean muy explícitos.
Compare transformar una matriz en otra con un bucle for:
y usando una
map
función de estilo JavaScript :O sumando una matriz:
versus:
¿Cuánto tiempo te lleva entender lo que hace?
versus
En los tres casos, aunque el bucle for es ciertamente legible, debe dedicar unos minutos para descubrir cómo se está utilizando el bucle for y verificar que todos los contadores y las condiciones de salida sean correctos. Las funciones modernas de estilo lambda hacen que los propósitos de los bucles sean extremadamente explícitos, y usted sabe con certeza que las funciones API que se están llamando se implementan correctamente.
La mayoría de los lenguajes modernos, incluidos JavaScript , Ruby , C # y Java , utilizan este estilo de interacción funcional con matrices y colecciones similares.
En general, si bien no creo que usar for loops sea necesariamente incorrecto, y es una cuestión de gusto personal, me he encontrado muy a favor de usar este estilo de trabajar con matrices. Esto se debe específicamente a la mayor claridad en la determinación de lo que está haciendo cada ciclo. Si su idioma tiene características o herramientas similares en sus bibliotecas estándar, le sugiero que considere adoptar este estilo también.
fuente
array.find
plantea la pregunta, ya que luego tenemos que discutir la mejor manera de implementararray.find
. A menos que esté utilizando hardware con unafind
operación incorporada , tenemos que escribir un bucle allí.find
en sus bibliotecas estándar. Sin lugar a dudas, estas bibliotecas implementanfind
y sus parientes usan bucles for, pero eso es lo que hace una buena función: abstrae los detalles técnicos del consumidor de la función, permitiendo que el programador no necesite pensar en esos detalles. Por lo tanto, aunquefind
es probable que se implemente con un bucle for, todavía ayuda a que el código sea más legible, y dado que con frecuencia está en la biblioteca estándar, su uso no agrega una sobrecarga o riesgo significativo.Todo se reduce a precisamente lo que se entiende por "mejor". Para programadores prácticos, generalmente significa eficiente, es decir, en este caso, salir directamente del bucle evita una comparación adicional, y devolver una constante booleana evita una comparación duplicada; Esto ahorra ciclos. Dijkstra está más preocupado por crear código que sea más fácil de probar que es correcto . [Me ha parecido que la educación en CS en Europa toma 'probar la corrección del código' mucho más en serio que la educación en CS en los EE. UU., donde las fuerzas económicas tienden a dominar la práctica de codificación]
fuente