La idea es que la resolución de un subproblema se abstrae mediante una clase de objetos. A estas clases se les llama usualmente tipos de datos abstractos (TDAs), por que son tipos de datos al igual que int, double, String, etc. Pero se agrega la palabra abstracto para enfatizar que los detalles de su implementación son desconocidos para el programador que usa el TDA.
Más adelante en el curso veremos cómo se definen nuevas clases de objetos. En la definición se especificarán los detalles de implementación de cada una de las operaciones de sus objetos. Al hacerlo el programador se abstrae del contexto en el que serán usados los objetos.
Por el momento sólo será importante ser capaz de entender la especificación de una clase de objetos para poder usarla en la resolución de un problema complejo.
Ejemplo:
La clase Fecha permite construir objetos que aceptan las siguientes operaciones:
Ejemplo | Significado | Encabezado |
---|---|---|
Fecha a= new Fecha(25, 12, 2000); | Construye una fecha para el dia 25/12/2000 | Fecha(int dd, int mm, int aaaa) |
if (a.comparar(b)<0) | 0 si a=b, <0 si a<b, >1 si a>b | int comparar(Fecha f) |
String dia= a.diaSemana() | entrega el día de la semana de a: "lunes" | String diaSemana() |
Fecha m= a.mannana(); | entrega la fecha de un día después de a | Fecha mannana() |
String s= a.toString(); | entrega un string en formato "dd/mm/aaaa" | String toString() |
La columna encabezado se usa para especificar el tipo de los parámetros de cada una de sus operaciones (métodos) y el tipo del resultado de la operación (más adelante veremos que éste es el encabezado que se usa al definir las operaciones en la clase).
Un ejemplo de uso de esta clase es un programa que despliega los días de semana que corresponden a los 7 primeros días del año 2000:
Ejercicio:
Fecha dia= new Fecha(1,1,2000);
Fecha siete= new Fecha(7,1,2000);
while (dia.comparar(siete)<=0) {
println(dia.toString()+": "+dia.diaSemana());
dia= dia.mannana();
}
El archivo feriados.txt contiene las fechas de los días
feriados del año 2000. En el archivo viene una línea por cada
feriado en el formato dd/mm. Además las fechas viene ordenadas.
1/1
21/4
22/4
1/5
21/5
...
Entre el Miércoles 1/1/2000 y el Jueves 21/5/2000: xxx dias
Entre el Jueves 21/5/2000 y el Lunes 4/6/2000: xxx dias
Ejemplo | Significado | Encabezado |
---|---|---|
WordParser decod= new WordParser("Esta noche puedo escribir"); | Construye un decodificador para "Esta ..." | WordParser(String s) |
String pal= decod.readString(); | Obtiene la primera palabra "Esta". | String readString(); |
String pal= decod.readString(); | Obtiene la segunda palabra "noche". | Idem |
if (decod.hasMoreWords()) ... | Determina si quedan más palabras en la frase | boolean hasMoreWords() |
Ejemplo de uso:
Colocar las primeras 1000 palabras de un archivo en un arreglo:
TextReader lect= new TextReader("palabras.txt");
String[] pals= new String[1000]; // No más de 1000
int i= 0;
while (i<1000) {
String lin= lect.readLine();
if (lect.eofReached()) {
break;
}
WordParser decod= new WordParser(lin);
while (decod.hasMoreWords() && i<1000) {
pals[i]= decod.readString();
i= i+1;
}
}
println("palabras leidas: "+i);
Ejemplo | Significado | Encabezado |
---|---|---|
StringMap map= new StringMap(); | Construye un arreglo asociativo | StringMap() |
map.put("pedro", 23); | Asocia el valor 23 a la llave "pedro" | void put(String llave, int val) |
map.put("pi", 3.14); | Asocia el valor 3.14 a la llave "pi" | void put(String llave, double x) |
void put(String llave, String s) | ||
void put(String llave, boolean b) | ||
int e= map.getInt("pedro"); | Obtiene el valor asociado a "pedro" | int getInt(String llave) |
double getDouble(String llave) | ||
String getString(String llave) | ||
boolean getBoolean(String llave) | ||
if (map.isMapped("pedro")) ... | Determina si se le ha asociado un valor a "pedro" | boolean isMapped(String llave) |
int n= map.size(); | Entrega el número de asociaciones en map | int size() |
StringKeyEnum enum= map.keys() | Entrega un enumerador (o iterador) de las llaves en map | StringKeyEnum keys() |
if (enum.hasMoreKeys()) ... | Determina si quedan llaves por visitar | boolean hasMoreKeys() |
String llave= enum.nextKey(); | Entrega una llave no visitada | String nextKey() |
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 ...
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
Crea un arreglo inicialmente vacío.
StringMap recuento= new StringMap();
Consulta
Para obtener la cantidad de votos captados por "Margaret Tatcher" se evalúa:
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.
int votos= recuento.getInt("Margaret Tatcher");
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:
Establecer una asociación
int votos= 0;
if (recuento.isMapped("Michael Jackson"))
votos= recuento.getInt("Michael Jackson");
Para establecer un nuevo valor para una llave en el arreglo se usa put:
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.
recuento.put("Michael Jackson", 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():
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).
StringKeyEnum enum= recuento.keys();
while (enum.hasMoreKeys()) {
String nombre= enum.nextKey();
...
}
Programa final:
(El programa completo se encuentra en Recuento.java y el archivo de votos en
votos.txt.)
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(nombre));
}
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:
Por otra parte, los casos en donde se necesitan llaves reales, se pueden resolver transformando las llaves en números enteros (multiplicando por ejemplo por 10, 100, etc.).
Recomendación: Utilice la clase Map cuando no sepa el tamaño del arreglo
que necesita crear (en vez de Array, int[], double[], etc.).
Map map= new Map(); // Sin especificar tamaño!
map.put(5, "cinco");
map.put(1000, "mil");
println(map.get(5));
KeyEnum enum= map.keys();
int i1= enum.nextKey(); // 5 o 1000, alguno de los dos
int i2= enum.nextKey(); // 1000 o 5
Los contenedores más usados son:
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.
Ejemplo | Significado | Encabezado |
---|---|---|
Queue cola= new Queue(); | Construye una cola | Queue() |
cola.put("pedro"); | Agrega "pedro" al final de la cola | void put(String s) |
cola.put("juan") | Agrega "juan" al final de la cola | Idem |
String s= cola.getString(); | Entre los elementos agregados a la cola, extrae y entrega el que está en primer lugar | String getString() |
if (cola.isEmpty()) ... | Determina si la cola está vacía | boolean isEmpty() |
La siguiente figura muestra el efecto que producen los métodos put y getString:
A la cola se pueden agregar también enteros, reales y booleans, los que se extraen con getInt(), getDouble y getBoolean.
Ejercicio:
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 n para agregar un paciente
Ingrese un s para atender un paciente
Ingrese un f para terminar
Opción ? n
Nombre del paciente ? juan
Opción ? n
Nombre del paciente ? pedro
Opción ? n
Nombre del paciente ? diego
Opción ? s
Atender al paciente: juan
Opción ? n
Nombre del paciente ? ana
Opción ? s
Atender al paciente: pedro
...
(El programa completo se encuentra en Pacientes.java.)
Queue pacientes= new Queue();
println("Ingrese una n para agregar un paciente");
println("Ingrese una s para atender un paciente");
println("Ingrese una f para terminar");
while (true) {
// Seleccionar una opcion
print("opcion ? ");
String opcion= readLine();
if (equals(opcion, "n")) {
// 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 (equals(opcion, "s")) {
// 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 (equals(opcion, "f")) {
break;
}
else {
println("Ingrese solo n, s o f");
}
}