Instituto de Computación. Facultad de Ingeniería. Universidad de la República Parcial de Programación 2 6 de Julio de 2012 • La prueba es individual y sin material; la duración es 3 hs; sólo se contestan dudas acerca de la letra de los ejercicios. • Escriba las hojas de un sólo lado y con letra clara; comience la solución de cada ejercicio en una nueva hoja; numere cada hoja, indicando en la primera el total; coloque su número de cédula y nombre en cada hoja. SI TODAS LAS RESTRICCIONES EXPLICITADAS DE UN EJERCICIO NO SON CUMPLIDAS POR LA SOLUCIÓN PROPUESTA, EL EJERCICIO TENDRÁ PUNTAJE 0. PRESTAR ATENCIÓN A LAS NOTAS INCLUÍDAS EN CADA EJERCICIO. • Ejercicio 1 (30 puntos: 5 parte a y 25 parte b) Parte a) Especifique un TAD Cola de Prioridad acotada de elementos de un tipo genérico T donde las prioridades estén dadas por números naturales y que permita obtener, y eliminar, elementos tanto de mínima como de máxima prioridad. Considere un conjunto mínimo de operaciones constructoras, selectoras/destructoras (Min, BorrarMin, Max, BorrarMax) y predicados. El TAD debe admitir la existencia de elementos de igual prioridad. Las selectoras/destructoras no deben considerar ningún criterio específico para elementos de igual prioridad. Esto es, no deben considerarse criterios de selección (desempate) entre elementos de igual prioridad. SOLUCIÓN (* Constructoras *) PROCEDURE Crear(): CP; * Retorna una cola de prioridad vacía. *) PROCEDURE Insertar(d: T; p: CARDINAL; VAR cp: CP ); (* Inserta en la cola de prioridad "cp" el dato "d" con prioridad "p". Precondición: NOT EstaLlena(cp) *) (* Selectoras/Destructoras *) PROCEDURE Min(cp: CP): T; (* Retorna el elemento de CP con menor prioridad. Precondición: NOT EsVAcia(cp) *) PROCEDURE BorrarMin(VAR cp: CP); (* Elimina el elemento de prioridad mínima de la cola de prioridad "cp". Precondición: NOT EsVAcia(cp) *) PROCEDURE Max(cp: CP): T; (* Retorna el elemento de CP con mayor prioridad. Precondición: NOT EsVAcia(cp) *) PROCEDURE BorrarMax(VAR cp: CP); (* Elimina el elemento de prioridad máxima de la cola de prioridad "cp". Precondición: NOT EsVAcia(cp) *) (* Predicados *) PROCEDURE EsVacia(cp: CP): BOOLEAN; (* Retorna TRUE si la cola de prioridad "cp" esta vacía. *) PROCEDURE EstaLlena(cp: CP): BOOLEAN; (* Retorna TRUE si la cola de prioridad "cp" esta llena. *) PROCEDURE DestruirCola(VAR cp: CP); (* Libera la memoria reservada por la cola cp y sus elementos *) Parte b) Desarrolle una implementación del TAD anterior donde las operaciones selectoras/destructoras (Min, BorrarMin, Max y BorrarMax) y los predicados tengan O(1) de tiempo de ejecución en el peor caso. Se debe dar una representación que permita además hacer un uso eficiente del espacio de almacenamiento para valores grandes de la cota del TAD, e implementar completamente las operaciones definidas en la parte (a). No se permite usar TADs auxiliares en este ejercicio. SOLUCIÓN CONST TOPE = ...; (* Algun valor apropiado *) TYPE CP = POINTER TO cabezalCP; nodoCP = POINTER TO celdaCP; celdaCP = RECORD prior: CARDINAL; dato: T; sig, ant: nodoCP; END; cabezalCP = RECORD primero, ultimo: nodoCP; contador: CARDINAL; END; (* Constructoras *) PROCEDURE Crear(): CP; (* Retorna una cola de prioridad vacía. *) VAR cabezal:CP; BEGIN NEW(cabezal); cabezal^.primero := NIL; cabezal^.ultimo := NIL; cabezal^.contador := 0; RETURN(cabezal); END Crear; PROCEDURE Insertar (d: CARDINAL; p: CARDINAL; VAR cp: CP ); (* Inserta ordenado en la cola de prioridad "cp" el dato "d" con prioridad "p". Precondición: NOT EstaLlena(cp) *) VAR aux, nuevo: nodoCP; sale: BOOLEAN; BEGIN NEW(nuevo); nuevo^.dato := d; nuevo^.prior := p; nuevo^.sig := NIL; nuevo^.ant := NIL; IF cp^.primero = NIL THEN cp^.primero := nuevo; cp^.ultimo := nuevo; ELSIF p <= cp^.primero^.prior THEN cp^.primero^.ant := nuevo; nuevo^.sig := cp^.primero; cp^.primero := nuevo; ELSE aux := cp^.primero; sale := FALSE; WHILE (NOT sale) AND (aux^.sig <> NIL) DO aux := aux^.sig; IF p <= aux^.prior THEN sale := TRUE; END; END; END; IF sale THEN aux^.ant^.sig := nuevo; nuevo^.sig := aux; nuevo^.ant := aux^.ant; aux^.ant := nuevo; ELSE aux^.sig := nuevo; nuevo^.ant := aux; cp^.ultimo := nuevo; END; cp^.contador := cp^.contador + 1; END Insertar; (* Predicados *) PROCEDURE EsVacia(cp: CP): BOOLEAN; (* Retorna TRUE si la cola de prioridad "cp" esta vacía. *) BEGIN RETURN (cp^.contador = 0); END EsVacia; PROCEDURE EstaLlena(cp: CP): BOOLEAN; (* Retorna TRUE si la cola de prioridad "cp" esta llena. *) BEGIN RETURN (cp^.contador = TOPE); END EstaLlena; (* Selectoras/Destructoras *) PROCEDURE BorrarMin(VAR cp: CP); (* Elimina el elemento de prioridad mínima de la cola de prioridad "cp". Precondición: NOT EsVAcia(cp) *) VAR nodoBorrar: nodoCP; BEGIN nodoBorrar := cp^.primero; IF cp^.contador = 1 THEN cp^.primero := NIL; cp^.ultimo := NIL; ELSE cp^.primero := nodoBorrar^.sig; cp^.primero^.ant := NIL; END; DISPOSE(nodoBorrar); cp^.contador := cp^.contador - 1; END BorrarMin; PROCEDURE Min(cp: CP): T; (* Retorna el elemento de CP con menor prioridad. Precondición: NOT EsVAcia(cp) *) BEGIN RETURN (cp^.primero^.dato); END Min; PROCEDURE BorrarMax(VAR cp: CP); (* Elimina el elemento de prioridad máxima de la cola de prioridad "cp". Precondición: NOT EsVAcia(cp) *) VAR nodoBorrar: nodoCP; BEGIN nodoBorrar := cp^.ultimo; IF cp^.contador = 1 THEN cp^.primero := NIL; cp^.ultimo := NIL; ELSE cp^.ultimo := nodoBorrar^.ant; cp^.ultimo^.sig := NIL; END; DISPOSE(nodoBorrar); cp^.contador := cp^.contador - 1; END BorrarMax; PROCEDURE Max(cp: CP): T; (* Retorna el elemento de CP con mayor prioridad. Precondición: NOT EsVAcia(cp) *) BEGIN RETURN (cp^.ultimo^.dato); END Max; PROCEDURE DestruirCola(VAR cp : CP); VAR borrar : CP; BEGIN borrar := cp^.primero; WHILE (cp^.primero <> NIL) DO cp^.primero := cp^.primero^.sig; DISPOSE(borrar); borrar := cp^.primero; END; DISPOSE(cp); END; Ejercicio 2 (30 puntos: 15 parte a y 15 parte b) Parte a) Considere el siguiente tipo Expresion, que representa expresiones enteras compuestas por: valores enteros, variables (cuyo nombre es un carácter) y suma de sub-expresiones. TYPE TipoExpresion = (Suma, Entero, Variable); Expresion = POINTER TO NodoExpresion; NodoExpresion = RECORD CASE tipo : TipoExpresion Suma : izq, der Entero : valor : Variable : nombre : END END; OF : Expresion | INTEGER | CHAR | Accediendo a la representación del tipo Expresion implemente, en Módula-2, el siguiente procedimiento: PROCEDURE CrearExpresion ( VAR tokens : ColaToken ) : Expresion; Que dada una cola de tokens no vacía, representando una expresión escrita en modo prefijo, genere el valor de tipo Expresion equivalente. Esta operación remueve de tokens todos los tokens consumidos para construir la expresión, destruyendo los tokens que remueve. Ejemplo, la cola de tokens: Genera la expresión: Considere implementados los TADs ColaToken y Token, donde un token puede ser un operador de suma, un entero o un caracter (nombre de variable). Especifique, con pre y post condiciones, las operaciones que utiliza de estos TADs. Solución PROCEDURE CrearExpresion (VAR tokens: ColaToken) : Expresion; VAR exp : Expresion; token : Token; BEGIN token := PrimeroColaToken(tokens); DesencolarColaToken(tokens); NEW(exp); IF EsOperadorSumaToken(token) THEN exp^.tipo := Suma; exp^.izq := CrearExpresion(tokens); exp^.der := CrearExpresion(tokens) ELSIF EsEnteroToken(token) THEN exp^.tipo := Entero; exp^.valor := ObtenerEnteroToken(token) ELSE exp^.tipo := Variable; exp^.nombre := ObtenerVariableToken(token) END; DestruirToken(token); RETURN exp END CrearExpresion; (******* Operaciones del TAD Token utilizadas en el ejercicio *******) PROCEDURE EsOperadorSumaToken(t : Token) : BOOLEAN; (* POST: Devuelve TRUE sii el token contiene un operador de suma. *) PROCEDURE EsEnteroToken(t : Token) : BOOLEAN; (* POST: Devuelve TRUE sii el token contiene un número entero. *) PROCEDURE EsVariableToken(t : Token) : BOOLEAN; (* POST: Devuelve TRUE sii el token contiene una variable. *) PROCEDURE ObtenerEnteroToken(t : Token) : INTEGER; (* PRE: EsEnteroToken(t) POST: Devuelve el número contenido en el token. *) PROCEDURE ObtenerVariableToken(t : Token) : CHAR; (* PRE: EsVariableToken(t) POST: Devuelve el caracter (la variable) contenido en el token. *) PROCEDURE DestruirToken(VAR t : Token); (* POST: Libera la memoria reservada por el token. *) (******* Operaciones del TAD ColaToken utilizadas en el ejercicio *******) PROCEDURE PrimeroColaToken (c: ColaToken): Token; (* PRE: NOT EsVaciaColaToken (c). POST: Devuelve el primer elemento de la Cola 'c'. *) PROCEDURE DesencolarColaToken (VAR c: ColaToken); (* PRE: NOT EsVaciaColaToken (c). POST: Quita el primer elemento de la Cola 'c'. *) Parte b) Accediendo a la representación del tipo Expresion implemente, en Módula-2, el siguiente procedimiento: PROCEDURE ReducirExpresion ( var : CHAR; val : INTEGER; expre : Expresion ) : Expresion; que retorna una expresión (sin compartir memoria) resultante de reducir una expresión (expre) todo lo que sea posible asignando un valor (val) a una variable (var). Por ejemplo, la expresión de la izquierda se reduce a la expresión de la derecha si var es 'x' y val es 1. Solución PROCEDURE ReducirExpresion(var: CHAR; val: INTEGER; exp: Expresion) : Expresion; VAR newExp: Expresion; sum : INTEGER; BEGIN NEW(newExp); CASE exp^.tipo OF Suma : newExp^.tipo := Suma; newExp^.izq := ReducirExpresion(var, val, exp^.izq); newExp^.der := ReducirExpresion(var, val, exp^.der); IF (newExp^.izq^.tipo = Entero) AND (newExp^.der^.tipo = Entero) THEN sum := newExp^.izq^.valor + newExp^.der^.valor; DISPOSE(newExp^.izq); DISPOSE(newExp^.der); newExp^.tipo := Entero; newExp^.valor := sum END; | Entero : newExp^.tipo := Entero; newExp^.valor := exp^.valor; | Variable : IF (exp^.nombre = var) THEN newExp^.tipo := Entero; newExp^.valor := val; ELSE newExp^.tipo := Variable; newExp^.nombre := exp^.nombre; END END; RETURN newExp END ReducirExpresion;