Tipos abstractos de datos

Anuncio
Problema: Agenda
Queremos programar una agenda de contactos.
De cada persona nos interesa guardar:
Introducción a la Computación
Primer Cuatrimestre de 2012
I
Nombre
I
Teléfono
I
Dirección
¿Cómo representamos los datos de las personas?
Tipos Abstractos de Datos
I
Nombres: arreglo de strings.
I
Teléfonos: arreglo de strings.
I
Direcciones: arreglo de strings.
Tales que la i-ésima posición de los 3 arreglos correspondan a la
misma persona. Esta representación funciona (cumple el objetivo),
pero tiene serios problemas...
1
Problema: Agenda
2
Problema: Contar dı́as entre 2 fechas
// Dice si el an~o a es bisiesto.
bool bisiesto(int a) {
return (a%4==0 && (a%100!=0 || a%400==0));
}
Mucho mejor serı́a poder definir un tipo Persona, que encapsule todos
los datos relevantes a una persona para nuestra agenda.
// Devuelve la cantidad de dias del mes m, an~o a.
int diasEnMes(int m, int a) {
int r=0;
if (m==1||m==3||m==5||m==7||m==8||m==10||m==12)
r = 31;
if (m==4||m==6||m==9||m==11)
r = 30;
if (m==2) {
if (bisiesto(a))
r = 29;
else
r = 28;
}
return r;
}
Tipo Abstracto de Datos (TAD) Persona
Operaciones:
I
CrearPersona(nom, tel, dir ) → Persona: Crea una persona nueva
con el nombre, teléfono y dirección especificados.
I
Nombre(p) → String: Devuelve el nombre de la persona p.
I
Teléfono(p) → String: Devuelve el teléfono de la persona p.
I
Dirección(p) → String: Devuelve la dirección de la persona p.
// Cuenta los dias entre los meses m1 y m2
// inclusive, en el an~o a.
int diasEntreMeses(int m1, int m2, int a) {
int r = 0;
int m = m1;
while (m <= m2) {
r = r + diasEnMes(m, a);
m = m + 1;
}
return r;
}
Ası́, podemos representar la agenda con un arreglo de Personas.
3
// Cuenta los dias entre el d1/m1/a1 y el d2/m2/a2.
int contarDias(int d1, int m1, int a1,
int d2, int m2, int a2) {
int r = 0;
if (a1 == a2 && m1 == m2)
r = d2 - d1;
if (a1 == a2 && m1 < m2) {
r = diasEnMes(m1, a1) - d1;
r = r + diasEntreMeses(m1+1, m2-1, a1);
r = r + d2;
}
if (a1 < a2) {
r = diasEnMes(m1, a1) - d1;
r = r + diasEntreMeses(m1+1, 12, a1);
int a = a1 + 1;
while (a < a2) {
r = r + 365;
if (bisiesto(a)) {
r = r + 1;
}
a = a + 1;
}
r = r + diasEntreMeses(1, m2-1, a2);
r = r + d2;
}
return r;
}
4
Problema: Contar dı́as entre 2 fechas
Modularidad y encapsulamiento
Imaginemos que contamos con un tipo Fecha que nos ofrece estas
operaciones (entre otras):
I
CrearFecha(d, m, a) → Fecha
I
FechaSiguiente(f ) → Fecha: Devuelve la fecha siguiente a la fecha
dada. (Ej: al 31/12/1999 le sigue el 1/1/2000.)
I
Menor(f1 , f2 ) → B: Devuelve TRUE si la fecha f1 es anterior que la
fecha f2 , y FALSE en caso contrario.
La clave está en encapsular los datos y sus operaciones.
Al programar, definimos funciones (ej: Primo(n)) para generar
código más simple y claro (código modular ).
Encapsulamos un algoritmo para poder reusarlo muchas veces.
Ahora generalizamos este concepto, y encapsulamos datos
(ej: personas, fechas) y sus operaciones (ej: FechaSiguiente(f)) en
Tipos Abstractos de Datos (TAD).
Usando esto, un algoritmo para contar dı́as podrı́a ser:
ContarDı́as(f1 , f2 ) → Z:
RV ← 0
while (Menor(f1 , f2 )) {
RV ← RV + 1
f1 ← FechaSiguiente(f1 )
}
Para usar un TAD, el programador sólo necesita conocer el nombre
del TAD y la especificación de sus operaciones.
Un TAD puede estar implementado de muchas formas distintas.
Esto debe ser transparente para el usuario.
6
5
TAD Lista(ELEM)
TAD Lista(ELEM)
Operaciones (cont.):
I
Operaciones:
I
CrearLista() → Lista(ELEM): Crea una lista vacı́a.
I
Agregar(x, L): Inserta el elemento x al final de L.
I
Longitud(L) → Z: Devuelve la cantidad de elementos de L.
I
Iésimo(i, L) → ELEM: Devuelve el i-ésimo elemento de L.
Precondición: 0 ≤ i < Longitud(L).
I
Insertar(i, x, L): Inserta el elemento x en la posición i de L, pasando
los elementos de la posición i y siguientes a la posición
inmediata superior. Pre: 0 ≤ i ≤ Longitud(L).
(Si L = a0 , .., an−1 , se convierte en L = a0 , .., ai−1 , x, ai , .., an−1 .)
I
BorrarIésimo(i, L): Borra el i-ésimo elemento de L
Precondición: 0 ≤ i < Longitud(L).
(Si L = a0 , .., an−1 , se convierte en L = a0 , .., ai−1 , ai+1 , .., an−1 .)
Cantidad(x, L) → Z: Devuelve la cantidad de veces que aparece el
elemento x en L.
I
donde L : Lista(ELEM), i : Z, x : ELEM (entero, char, etc.).
Indice(x, L) → Z: Devuelve el ı́ndice (0 .. Longitud(L) − 1) de la
primera aparición de x en L. Pre: Cantidad(x, L) > 0.
donde L : Lista(ELEM), i : Z, x : ELEM (entero, char, etc.).
... y agregamos cualquier otra función que consideremos útil.
7
8
TAD Lista(ELEM) - Posible implementación
TAD Lista(ELEM) - Posible implementación
Vamos a representar una lista como una cadena de nodos.
Definamos primero qué es un nodo.
Primero elegimos una estructura de representación del TAD
Lista(ELEM) (que es privada).
Tipo Nodo(ELEM): hvalor : ELEM, siguiente : Ref(Nodo)i
Lista(ELEM) == hcabeza : Ref(Nodo), longitud : Zi
h...i define una tupla.
Después describimos el invariante de representación de esta
estructura. En este caso longitud siempre debe ser igual a la
cantidad de nodos encadenados a partir de cabeza, y en la cadena
de nodos no deben formarse ciclos.
Ref(Nodo) es una referencia a una instancia de tipo Nodo. Puede
tomar el valor especial Nil (“referencia a nada”).
Construcción de un nuevo Nodo:
I
Por último, damos los algoritmos de las operaciones del TAD
Lista(ELEM) para la estructura de representación elegida:
n ← Nodo(x, r )
Acceso a los campos de un nodo n:
I
n.valor devuelve el campo valor .
I
n.siguiente devuelve el campo siguiente.
CrearLista() → Lista(ELEM):
RV .cabeza ← Nil
RV .longitud ← 0
9
TAD Lista(ELEM) - Posible implementación
10
TAD Lista(ELEM) - Posible implementación
Agregar(x, L):
nuevo ← Nodo(x, Nil)
if (L.cabeza = Nil) {
L.cabeza ← nuevo
} else {
aux ← L.cabeza
while (aux.siguiente 6= Nil) {
aux ← aux.siguiente
}
aux.siguiente ← nuevo
}
L.longitud ← L.longitud + 1
Longitud(L) → Z:
RV ← L.longitud
Iésimo(i, L) → ELEM:
aux ← L.cabeza
while (i > 0) {
aux ← aux.siguiente
i ←i −1
}
RV ← aux.valor
(Pre: 0 ≤ i < Longitud(L))
Y ası́ con las otras operaciones del TAD Lista(ELEM): Cantidad,
Insertar, BorrarIésimo e Indice.
11
12
Partes de un Tipo Abstracto de Datos
I
Parte pública: Disponible para el usuario externo.
I
I
I
Problema: Paréntesis balanceados
Nombre y tipos paramétricos (ej: Lista(ELEM)).
Operaciones y sus especificaciones (encabezado, precondición,
poscondición).
Dado un string, determinar si los caracteres { }, [ ], ( ) están
balanceados correctamente.
Ejemplos:
Parte privada: Sólo accesible desde dentro del TAD. ¡El
usuario externo nunca debe ver ni meterse en esto!
I
I
I
Estructura de representación del TAD.
Invariante de representación: qué propiedades debe cumplir
la estructura elegida para que tenga sentido como la
representación de una instancia del TAD.
Algoritmos de las operaciones para la estructura de
representación elegida.
I
“{a(b)x[()]}” está balanceado.
I
“}”, “a(b))” y “[[})” no están balanceados.
¿Se les ocurre una solución?
14
13
TAD Pila(ELEM)
Problema: Paréntesis balanceados
Dado un string, determinar si los caracteres { }, [ ], ( ) están
balanceados correctamente.
Operaciones:
I
CrearPila() → Pila(ELEM): Crea una pila vacı́a.
I
Apilar(x, P): Inserta el elem. x sobre el tope de la pila P.
I
Vacı́a?(P) → B: Dice si la pila P está vacı́a.
I
Tope(P) → ELEM: Devuelve el elemento del tope de P.
Precondición: ¬Vacı́a?(P).
I
Desapilar(P): Borra el elemento del tope de P.
Precondición: ¬Vacı́a?(P).
¿Se les ocurre una solución usando el tipo Pila?
p ← CrearPila()
Para cada caracter c en el string de entrada:
Si c es ‘{’, ‘[’ o ‘(’:
Apilar(c, p)
Si c es ‘}’, ‘]’ o ‘)’:
d ← Tope(p)
Desapilar(p)
Si c no se corresponda con d: responder que no.
Si Vacı́a?(p), responder que sı́. Si no, responder que no.
donde P : Pila(ELEM), x : ELEM (entero, char, etc.).
Las pilas tienen estrategia LIFO (last in, first out).
15
16
Repaso de la clase de hoy
I
Tipos Abstractos de Datos.
I
I
I
Parte pública: nombre + operaciones.
Parte privada: estructura, invariante, algoritmos.
Lista(ELEM), Pila(ELEM).
Temas de la clase que viene
I
Una posible implementación del TAD Pila(ELEM).
I
TAD Cola(ELEM).
I
TAD Conjunto(ELEM).
17
Descargar