Patrones de programación

Objetivos: Mostrar que en los programas existen patrones de instrucciones que se repiten una y otra vez, y que son útiles para resolver problemas típicos de codificación de los programas.

Temas:


Patrones de programación

Programar no significa colocar líneas al azar y ver si se obtienen los resultados requeridos. Tampoco significa concebir una solución partiendo desde cero. Siempre se reutilizan elementos de programas conocidos. Es así como al estudiar los programas se pueden observar patrones de organización de las instrucciones que se repiten una y otra vez. Estas instrucciones rara vez son idénticas, pero exhiben ciertas similitudes, que denominaremos patrones de programación.

Es importante conocer estos patrones de programación porque son muy útiles para concebir nuevos programas. Ellos resuelven problemas conocidos sin tener que re-inventar soluciones para esos problemas una y otra vez.

Un ejemplo de estos patrones de programación es la acumulación. Este patrón se usa para realizar cálculos como la suma de varios valores calculados en las iteraciones de un ciclo:

suma= val1 + val2 + val3 + ... + valn
producto= fac1 * fac2 * fac3 * ... * facn

La forma general de este patrón es:

    ``tipo'' ``variable'' = ``valor inicial'';
    ...
    while ( ... ) {
      ...
      ``variable'' = ``variable'' ``operador'' ``expresión'';
      ...
    }
La variable utilizada en este patrón se denomina acumulador.

Ejemplos:

  1. Contar cuantos números ingresa el usuario, hasta que ingrese el 0:

        ? 4.0
        ? 5.0
        ? 6.0
        ? 0.0
        Contador= 3
    
    Solución:

        int cont= 0;            double suma= 0.0;
        double nota; 
        print("? ");
        nota= readDouble();
        while (nota!=0.0) {
          cont= cont+1;         suma= suma + nota;
          print("? ");
          nota= readDouble();
        }
        print("Contador= ");    print("Suma= ");
        println(cont);          println(suma);
    
    Observe la similitud de esta solución con la que calcula la suma de las notas.

  2. Calcular el promedio de notas:

        ? 4.0
        ? 5.0
        ? 6.0
        ? 0.0
        Promedio= 5.0
    
    Solución: la idea es condensar ambos programas en uno solo. El programa cuenta cuantas notas se han ingresado y suma las notas que se han ingresado en cada iteración.

        double suma= 0;
        int cont= 0;
        double nota;
        print("? ");
        nota= readDouble();
        while (nota!=0.0) {
          suma= suma+nota;
          cont= cont+1;
          print("? ");
          nota= readDouble();
        }
        print("Promedio= ");
        println(suma/cont);
    
    En este programa se presenta también el patrón de lectura de datos. Su forma general es:

        ``tipo'' ``variable'';
        ...
        ``variable''= read``tipo''();
        while ( ... ) {
          ...
          ``variable''= read``tipo''();
        }
    
    Típicamente marcaremos el fin de los datos con un cero o -1.

  3. Desplegar los números enteros de 1 a n:

        ? 4
        1
        2
        3
        4
    
    Solución: nuevamente usamos el patrón de acumulación.
        print("? ");
        int n= readInt();
        int i= 1;
        while (i<=n) {
          println(i);
          i= i+1;
        }
    
    Acá, el programa termina cuando la variable i excede en valor a n. En realidad, estamos acá en presencia de otro patrón muy utilizado: el patrón de conteo. Este patrón constituye un caso particular del patrón de acumulación. Su forma general es:

        int i= ``valor inicial'';
        while ( i <= ``valor final'' ) {
          ...
          i= i + 1;
        }
    
    La variable i se denomina en este caso un contador y toma valores entre [``valor inicial'',``valor final''], incrementándose en 1 en cada iteración.

  4. Calcular el factorial de un número:

    Definición matemática:

    0! = 1
    n! = (n-1)! cuando n>=1

    O informalmente:

    fact= 1 * 2 * 3 * ... * (n-1) * n

    Solución: al programa anterior le agregamos el producto de los valores sucesivos que toma la variable i en cada iteración. Para hacer el cálculo usamos el patrón acumulación con el operador *.

        print("? ");
        int n= readInt();
        double fact= 1.0;
        int i= 1;
        while (i<=n) {
          fact= fact*i;
          i= i+1;
        }
        print("El factorial es ");
        println(fact);
    
Ejercicio 1:

Ejecute paso a paso el programa anterior. Suponga que el usuario ingresa el número 3.


Ejercicio 2:

Calcular exp(x) por medio de la siguiente aproximación.

exp(x)= 1 + x + x^2/2! + x^3/3! + ... + x^i/i! + ... + x^n/n!

Solución:

El siguiente programa calcula i! al mismo tiempo que calcula x^i.

    double xi= 1.0;
    double facti= 1.0;
    int i= 1;
    while (i<=n) {
      xi= xi*x;
      facti= facti*i;
      i= i+1;
    }
Observe que en la i-ésima iteración, la variable xi es x^i y facti es i!.

A este programa podemos agregarle una acumulación de xi/facti:

    double x= ...; // Obtener x y n
    int n= ...;
    double xi= 1.0;
    double facti= 1.0;
    double expx= 1.0;
    int i= 1;
    while (i<=n) {
      xi= xi*x;
      facti= facti*i;
      expx= expx + xi/facti;
      i= i+1;
    }
Ahora basta desplegar el resultado final con la instrucción:

    println(expx);

Ejercicio 3:

Haga un programa que lea varios números positivos (terminados con un -1) e indique en qué rango se encontraban (exceptuando el -1). El diálogo debe ser:

    ? 5
    ? 3
    ? 20
    ? 10
    ? -1
    El rango es [3,20]
Solución:

    int num;
    int max;
    int min;
    print("? ");
    num= readInt();
    max= num;
    min= num;
    while (num!=0) {
      if (num>max) max= num;
      if (num<min) min= num;
      print("? ");
      num= readInt();
    }
    print("El rango es [");
    print(min); print(",");
    print(max); println("]");
Observe que las instrucciones que están en negritas corresponden a una variación del patrón de acumulación. No es exactamente el patrón de acumulación, pero se asemeja. Si hubiésemos escrito:

      max= max(max,num);
sería aún más parecido. El operador del patrón de acumulación sería en este caso max.

El programa también se puede escribir como:

    int num;
    int max= 0; // o MININT
    int min= MAXINT;
    print("? ");
    num= readInt();
    while ( ... ) { ... }
    ...
Ejecute paso a paso dos iteraciones de este programa y se convencerá de que está correcto. MAXINT es una constante entera que es más grande que cualquier otro entero en Java.

En realidad, los enteros en Java tienen un valor máximo (y un mínimo para los negativos). El primer número que se lea forzosamente será menor (o igual) a MAXINT y quedará por lo tanto almacenado en min ya en la primera iteración.