domingo, 2 de noviembre de 2014

Estándares de programación: Preferiblemente usa bucles "foreach"

En cualquier lenguaje de programación hay una serie de estructuras básicas para realizar un bucle, en esta publicación analizaremos el bucle for y el bucle foreach, y la conveniencia de usar el segundo sobre el primero, el código mostrado sera en C# (aunque debiera ser semejante en cualquier otro lenguaje).

Comencemos viendo los tipos de bucle mas comunes:
  • Esta el bucle while:
1
2
3
4
while (SeCumplaCondicion())
{
}
while (SeCumplaCondicion())
{

}
Es el más básico, y se ejecutaría hasta que algo hiciera que se cumpla la condición.
  • bucle while tipo dos:

1
2
3
do {
} while (SeCumplaCondicion())
do {

} while (SeCumplaCondicion())
Garantiza que por lo menos se realiza una iteración del bucle antes de comprobar si se cumple la sentencia, por lo demás es igual que el anterior
  • El bucle for:


1
2
3
4
for (int i = 0; i < 10; i++)
{
}
for (int i = 0; i < 10; i++)
{

}
Este bucle se usa para recorrer algún tipo de array, o una colección, teniendo esta estructura

1
2
3
4
5
6
List<string> nombres = new List<string>() { "JUAN", "PEDRO", "ANA", "INES" };
for (int i = 0; i < nombres.Count; i++)
{
    Console.WriteLine(" Nombre :" + nombres[i]);
}
List<string> nombres = new List<string>() { "JUAN", "PEDRO", "ANA", "INES" };

for (int i = 0; i < nombres.Count; i++)
{
    Console.WriteLine(" Nombre :" + nombres[i]);
}
El resultado será algo parecido a:


  • Nombre :JUAN
  • Nombre :PEDRO
  • Nombre :ANA
  • Nombre :INES

Este bucle tiene varios problemas asociados:

lo primero es que usamos un numero para acceder a la colección, si nos fijamos nuestra intención no es trabajar con la posición de los nombres dentro del objeto, es decir nos da igual si es la primera o la ultima, nosotros queremos recórrelos todos en secuencia uno a uno. Su posición para nosotros es irrelevante. Esto es importante, por que se pierde el verdadero objetivo del código, hacer algo con todos los elementos del array, al margen de su posición. Requiera además que ya se sepan todos los nombres (los elementos de la colección) antes de procesar el bucle, en algunos casos no requeríamos conocer todo el escenario antes de querer procesarlo, por ejemplo, si estamos buscando un archivo en todo el disco duro, no sería recomendable que primero listemos todos los archivos y luego lo recorriéramos buscando el archivo idóneo.

  • El último bucle, es el bucle foreach, tiene la siguiente estructura


List<string> nombres = GenerarListaNombres();

1
2
3
4
5
6
List<string> nombres = GenerarListaNombres();
foreach (string nombre in nombres)
{
    Console.WriteLine(" Nombre :" + nombre);
}
List<string> nombres = GenerarListaNombres();

foreach (string nombre in nombres)
{
    Console.WriteLine(" Nombre :" + nombre);
}
En esta forma de bucle, se elimina el uso de los índices (int i), lo que simplifica su código y nos permite centrarnos en el objetivo real, que es recorrer los elementos de la lista.

El hecho de que el código sea más sencillo, lo hace a su vez mas mantenibles y escalable en un futuro.

Por otro lado el único requisito es que el elemento a recorrer implemente la interfaz IEnumerable, lo cual nos permite realizar ajustes a como se procesan los elementos, en este ejemplo se recorren los nombres hasta que se encuentra la palabra "PEDRO"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void Ejemplo2()
{
    foreach (string nombre in GenerarListaNombresEnumerable())
    {
        Console.WriteLine(" Nombre :" + nombre);
        if (nombre == "PEDRO") break;
    }
}
public static IEnumerable<string> GenerarListaNombresEnumerable()
{
    yield return "JUAN";
    yield return "PEDRO";
    yield return "ANA";
    yield return "INES";
}
public static void Ejemplo2()
{
    foreach (string nombre in GenerarListaNombresEnumerable())
    {
        Console.WriteLine(" Nombre :" + nombre);
        if (nombre == "PEDRO") break;
    }
}

public static IEnumerable<string> GenerarListaNombresEnumerable()
{
    yield return "JUAN";
    yield return "PEDRO";
    yield return "ANA";
    yield return "INES";
}
Esta función especial que devuelve los campos con la palabra yield, nos permite genera la lista de nombres al mismo tiempo que se está procesando, la ventaja es que no se generan nombres que no se van a usar (por ejemplo no se genera "ANA", ni "INES", en caso que poníamos anteriormente, si estuviéramos buscando un archivo por todo el disco duro, podríamos detener la búsqueda cuando encontráramos el archivos requerido, y no tendríamos primero que localizar todo los archivos y ver si cumplen las características que deseamos uno a uno.

Adicionalmente el listado de nombre no se puede modificar dentro de un bucle foreach, con lo que garantiza seguridad de no alteración del listado (los objetos dentro del listado si se puede mejorar).

Resumiendo las ventajas del bucle foreach, quedarían las siguiente:

  • Código más limpio y sencillo, que expresa mejor las intenciones del codificador.
  • No es necesario generar completamente los elementos a recorrer, antes de comenzar a recorrerlos.
  • No es posible modificar el listado original.


¿Cuándo usar un bucle for o una foreach?, en principio la mejor opción es siempre el bucle foreach, por los motivos establecidos, (sobre todo el de claridad en el código), salvo en el caso que realmente sea relevante la posición del elemento en la lista.

No hay comentarios:

Publicar un comentario