Lunes 21 de Junio

Arreglos Asociativos con LLaves de Tipo String

Objetivos: Extender el concepto de arreglo asociativo con llaves de tipo entero a arreglos con llaves de tipo String.

Temas:


La clase StringMap

En los arreglos de la clase Map, las llaves sólo pueden ser de tipo entero. La clase StringMap es equivalente a la clase Map, sólo que las llaves deben ser de tipo String.

Ejemplo:

    StringMap tab= new StringMap();
    tab.put("pedro", 1.75);
    tab.put("alberto", 1.9);
    println(tab.getDouble("pedro")); // 1.75
    println(tab.getDouble("pedro")); // 1.9
Ejercicio:

El archivo "votos.txt" contiene las preferencias de los electores en una elección. Cada línea contiene el nombre del candidato preferido por un elector. No se conoce los nombres de los candidatos. Escriba un programa que haga el recuento de votos. La salida del programa debe ser:

    Margaret Tatcher: 20 votos
    Boris Yeltsin: 9 votos
    Ronald Reagan: 17 votos
    ... etc ...
Solución:

Se usa el arreglo asociativo recuento para mantener el número de votos de cada candidato. La idea es que recuento.getInt("xxx") entregará la cantidad de votos percibidos por el candidato "xxx".

Creación

    StringMap recuento= new StringMap();
Crea un arreglo inicialmente vacío.

Consulta

Para obtener la cantidad de votos captados por "Margaret Tatcher" se evalúa:

    int votos= recuento.getInt("Margaret Tatcher");
Es decir, se usa el nombre del candidato como llave en el arreglo. El valor asociado al nombre es entonces su número de votos.

Existencia de una llave

Al obtener el valor asociado a una llave, se debe haber establecido previamente un valor para esa llave en el arreglo. Como éste no será el caso la primera vez que se encuentre el nombre de un candidato en el archivo, se deberá consultar primero si esa llave existe en el arreglo mediante:

    int votos= 0;
    if (recuento.isMapped("Michael Jackson"))
       votos= recuento.getInt("Michael Jackson");
Establecer una asociación

Para establecer un nuevo valor para una llave en el arreglo se usa put:

    recuento.put("Michael Jackson", votos+1);
Si la llave no existía previamente en el arreglo, se crea la llave y se establece la asociación. Si la llave sí existía previamente, se cambia su valor asociado por el resultado de votos+1.

Recorrido de las llaves de un StringMap

Después de leer todo el archivo y contabilizar todos los votos, es necesario conocer todas las llaves presentes en el arreglo. Para esto se obtiene un enumerador de las llaves del arreglo invocando el método keys():

    StringKeyEnum enum= recuento.keys();
    while (enum.hasMoreKeys()) {
      String nombre= enum.nextKey();
      ...
    }
En cada iteración, la variable nombre corresponderá al nombre de un candidato. Cada nombre aparecerá una sola vez. Para averiguar cuantos votos obtuvo se evalúa recuento.getInt(nombre).

Programa final:

    StringMap recuento= new StringMap();
    TextReader lect= new TextReader("votos.txt");
    while (true) { // Cuenta las apariciones
      String nombre= lect.readLine();
      if (lect.eofReached())
        break;
      int votos= 0;
      if (recuento.isMapped(nombre))
        votos= recuento.getInt(nombre);
      recuento.put(nombre, votos+1);
    }
    // Muestra los resultados
    StringKeyEnum enum= recuento.keys();
    while (enum.hasMoreKeys()) {
      String nombre= enum.nextKey();
      println(nombre+": "+recuento.getInt(num));
    }
Por lo tanto, para la clase StringMap se aplican los mismos patrones de programación que para la clase Map. La única diferencia radica en que las llaves deben ser de tipo String.

Observaciones:


Contenedores

Los arreglos asociativos corresponden a una categoría de objetos denominados contenedores. Estos objetos almacenan valores (u objetos), pero en sí no realizan trabajo útil. El trabajo útil se realiza con los valores que almacenan. El arreglo asociativo es el más poderoso de los contenedores, por la cantidad de problemas en los que se puede usar. Sin embargo, existen otros contenedores más simples y que en ocasiones resultan más cómodos de usar.

Los contenedores más usados son:

Los nombres se usan como metáforas de estructuras que se dan en la vida real. Por ejemplo, una cola es un remedo de lo que ocurre en una cola de personas esperando ser atendidas en un banco. Cuando llega una persona, se coloca al final de la cola y sólo será atendida cuando hayan sido atendidas todas las personas que llegaron antes que él.

De la misma forma, una pila es un remedo de lo que ocurre con una pila de papeles encima del escritorio. Si se coloca un papel encima de la pila, éste se puede extraer primero. El que está en el fondo de la pila, sólo se podrá extraer cuando se hayan extraído todos los que están encima de él.


La clase Queue

Problema

En una consulta médica, se necesita un programa que indique el orden de atención de los pacientes. Los pacientes deben atenderse en orden de llegada (el primero en llegar es el primero en ser atendido). Al llegar un paciente, éste indica su nombre a la secretaria que opera el computador. Cuando el médico queda libre, la secretaria consulta en el computador cuál es el próximo paciente que debe ser atendido.

El programa debe responder al siguiente diálogo:

    Ingrese un 1 para agregar un paciente
    Ingrese un 2 para atender un paciente
    Ingrese un 3 para terminar
    Opción ? 1
    Nombre del paciente ? juan
    Opción ? 1
    Nombre del paciente ? pedro
    Opción ? 1
    Nombre del paciente ? diego
    Opción ? 2
    Atender al paciente: juan
    Opción ? 1
    Nombre del paciente ? ana
    Opción ? 2
    Atender al paciente: pedro
Solución.

Para resolver este problema usaremos una cola. Una cola es un contenedor que ofrece métodos para agregar elementos al final de la cola y para extraer el elemento que se encuentra en primer lugar en la cola.

Creación:

    Queue pacientes= new Queue();
Crea una cola inicialmente vacía.

Agregar un elemento:

    pacientes.put("ana");
Agrega "ana" al final de la cola. Para poder extraer "ana", se deben extraer primero todos los elementos que se hayan agregado antes que "ana". También se pueden agregar valores de tipo int, double o boolean.

Extraer un elemento de la cola:

    String nombre= pacientes.getString();
Extrae el primer elemento de la cola. No se puede extraer el segundo elemento sin haber extraído previamente el primero. La siguiente figura muestra el efecto que producen los métodos put y getString:

También existen getInt(), getDouble y getBoolean para extraer valores de otros tipos.

Consultar si la cola está vacía:

Antes de extraer un elemento de la cola hay que verificar que la cola no este vacía, porque se produce un error en tiempo de ejecución cuando se intenta extraer un elemento de una cola vacía.

    if (pacientes.isEmpty())
      println(pacientes.getString());
    else
      println("no hay pacientes en espera");
Programa:

    Queue pacientes= new Queue();
    println("Ingrese un 1 para agregar un paciente");
    println("Ingrese un 2 para atender un paciente");
    println("Ingrese un 3 para terminar");
    while (true) {
      // Seleccionar una opcion
      print("opcion ? ");
      int opcion= readInt();
      if (opcion==1) {
        // Ha llegado un nuevo paciente.  Se pide el nombre y se agrega
        // a la cola.
        println("Nombre del paciente ? ");
        String nombre= readLine();
        pacientes.put(nombre);
      }
      else if (opcion==2) {
        // El medico queda disponible para atender un nuevo paciente.
        // El nombre del proximo paciente es aquel que se encuentra
        // en primer lugar en la cola.
        if (pacientes.isEmpty())
          println("No hay pacientes en espera");
        else {
          String nombre= pacientes.getString();
          println("Atender al paciente: "+nombre);
        }
      else if (opcion==3)
        break;
      else
        println("La opcion es invalida");
    }