Lunes 31 de Mayo

Definición de Procedimientos

Objetivos: Mostrar para qué sirven y cómo se pueden definir procedimientos.

Temas:


Ejercicio:

Defina una función mayor que calcule el máximo valor en un arreglo. Por ejemplo, si el arreglo tab contiene las siguientes asociaciones:

Llave Valor
1 4.5
2 15.0
3 2.2
4 4.3

entonces:

    println( mayor(tab, 1, 4) ); // despliega 15.0
    println( mayor(tab, 3, 4) ); // despliega 4.3
El primer argumento es el arreglo, el segundo es la llave inicial en el arreglo y el tercero es la llave final.

Solución:

    double mayor(Map tab, int pri, int ult) {
      int i= pri;
      double max= tab.getDouble(pri);
      while (i<=ult) {
        if (tab.getDouble(i)>max)
          max= tab.getDouble(i);
        i= i+1;
      }
      return max;
    }
Este ejemplo muestra que una función puede recibir como argumentos referencias a objetos. En otros ejemplos veremos que una función puede entregar (retornar) referencias a objetos.

Observación:

Los métodos getTipo(llave) requieren que la llave exista en el arreglo. Si la llave no existe, se produce un error. Por ejemplo, la siguiente invocación de la función mayor produce un error en tiempo de ejecución:

    println( mayor(tab, 2, 5) ); // error!

Qué es un procedimiento

Un procedimiento es similar a una función, pero difiere en que no entrega ningún resultado. Su interés radica en las acciones que se realizan durante su invocación. Un ejemplo de procedimiento es println(...). Éste siempre se invoca en forma aislada (no como parte derecha en una asignación):
    println( ... );
Las acciones que realiza la invocación de println consisten en desplegar en pantalla el argumento que recibe. Es ilegal escribir asignaciones como:

    x= println( ... );
porque println no retorna ningún valor.


Definición de procedimiento

Un procedimiento se define y se comporta de la misma forma que una función. La única diferencia es que el tipo retornado por un procedimiento es void. Por ejemplo, el siguiente procedimiento despliega los elementos de un arreglo con valores reales:

    void mostrar(Map tab, int pri, int ult) {
      int i= pri;
      while (i<=ult) {
        println(i+" -> "+tab.getDouble(i));
        i= i+1;
      }
      // no hay return
    }
Ejemplos de uso de mostrar son los siguientes:

    mostrar(tab, 1, 4);
que produce el siguiente resultado en pantalla:

    1 -> 4.5
    2 -> 15.0
    3 -> 2.2
    4 -> 4.3
Ejercicio: procedimiento que graba el contenido de un arreglo en un archivo.

Defina el procedimiento grabar que reciba como argumentos un arreglo con valores de tipo double, dos enteros que señalan el rango de llaves válidas (pri y ult) y el nombre de un archivo. Su procedimiento debe producir un archivo con el contenido del arreglo. Por ejemplo, la siguiente invocación:

    grabar(tab, 1, 4, "cont.txt");
debe producir un archivo de nombre "cont.txt" con 4 líneas cuyo contenido es:

    4.5
    15.0
    2.2
    4.3

Procedimientos con efectos laterales

Definir un procedimiento leer que recibe como argumentos un arreglo vacío y el nombre de un archivo, e inicializa el arreglo con los valores reales contenidos en un archivo. Las llaves parten de 1 y terminan en el número de líneas del archivo. Por ejemplo:

    Map tabB= new Map(); // tabB está vacío
    leer(tabB, "cont.txt");
    mostrar(tabB, 1, 4);
debe producir el siguiente resultado en pantalla:

    1 -> 4.5
    2 -> 15.0
    3 -> 2.2
    4 -> 4.3
Solución:

    void leer(Map tab, String nom) {
      TextReader lect= new TextReader(nom);
      int i= 0;
      while (true) {
        double x= lect.readDouble();
        if (lect.eofReached())
          break;
        i= i+1;
        tab.put(i, x);
      }
      lect.close();
    }
Una función o procedimiento puede modificar los arreglos que recibe como argumentos. En general, pueden producir efectos laterales sobre sus parámetros, cuando son algún tipo de objetos.

En una función o procedimiento, los parámetros que corresponden a algún tipo de objetos son en realidad referencias de objetos (recuerde que una referencia es como el teléfono celular del objeto). Por lo tanto, dentro del procedimiento leer, la variable tab referencia el mismo objeto que es referenciado por la variable tabB en la invocación de leer:


Funciones con efectos laterales

Observación: el procedimiento anterior no permite saber cuantos valores se leyeron en el archivo. Por esta razón, es conveniente transformarlo en una función que retorna cuantos elementos habían en el archivo. Por ejemplo:

    Map tabC= new Map(); // tabC está vacío
    int ult= leer(tabC, "cont.txt");
    mostrar(tabC, 1, ult);
Para esto hay que cambiar la definición de leer por:

    int leer2(Map tab, String nom) {
      ... // idéntico a leer
      return i; // la última llave inicializada
    }
En la primera versión de leer la asignación:

    int ult= leer(tabB, "cont.txt");
es ilegal porque se definió leer como void. Con la segunda versión sí es válida esta asignación.


Observación:

¿Qué despliega el siguiente programa?

    int i=1;
    Map tabD= new Map();
    int ult= leer2(tabD, "cont.txt");
    println("leidos= "+i);
En este caso, el valor desplegado para i es 1. La variable i que usa la función leer es interna a esa función y no tiene relación alguna con la variable i recién escrita. Diremos que la variable i declarada internamente en la función leer es invisible fuera de la función.


Funciones que retornan objetos

Una función puede retornar objetos. Por ejemplo, se puede construir una función que construye un arreglo y lo entrega a su llamador (el que invoca la función):

    Map tabE= leer3("cont.txt");
    mostrar(tab3);
en donde leer3 es una función que recibe el nombre de un archivo y construye un arreglo asociativo con los valores almacenados en el archivo. Se como:

    Map leer3(String nom) {
      Map tab= new Map();
      ... // idéntico a leer
      return map; // la última llave inicializada
    }
Esta función crea un objeto de tipo Map, lo inicializa con valores leídos de un archivo (cuyo nombre está almacenado en nom) y al final retorna la referencia del arreglo.