Clase Práctica 2: Especificación

Anuncio
Algoritmos y Estructuras de Datos I
1
Clase Práctica 2: Especificación - 22/08/14 (Tarde)
De la Lógica Proposicional a la Especificación
Sea una especificación:
problema probleNom(param1 : tipo1, ..., paramN : tipoN ) = res : tipoRes{
requiere : preCond1;
requiere : preCond2;
asegura : posCond1;
asegura algunNombre: posCond2;
aux f uncAux(paramA : tipoA, ..., paramZ : tipoZ) : tipoExpr = expresion;
}
¿Cómo se la ve desde el punto de vista lógico?
preCond1 ∧ preCond2 → posCond1 ∧ posCond2
Podemos ver a la fórmula correspondiente a una especificación como un implica, donde el antecedente
es la conjunción (en orden según se escribieron) de las precondiciones, y el consecuente la conjunción
de las poscondiciones. Luego, la evaluación de la fórmula es por semántica de cortocircuito. Si la
conjunción de las precondiciones es falsa, entonces no importa lo que suceda con las poscondiciones
porque la implicación será verdadera (y no se sigue evaluando). Por eso, en ese caso no importa si
los asegura dan falso, verdadero o se indefinen... cualquier algoritmo va a cumplir. En cambio, si las
precondiciones son verdaderas, las poscondiciones también deberán serlo para que la especificación se
cumpla (y no se viole el contrato).
2
Ejercicios introductorios de Especificación
Ejercicio 1. Especificar una función que, dados dos parámetros a y b enteros:
i) devuelva el cociente entero entre ambos.
problema cociente(a, b : Z) = res : Z{
requiere : b 6= 0;
asegura : res == a div b;
}
ii) devuelva el cociente entero en a y el resto en b.
problema cociente(a, b : Z){
requiere : b 6= 0;
modifica a, b;
asegura : a == pre(a) div pre(b);
asegura : b == pre(a) mod pre(b);
}
Observación: Hacemos uso de las operaciones del tipo de datos Z. En el segundo caso, en vez de
devolver el resto, ¿podrı́amos devolver en b un valor de verdad según si la división fue exacta? No, pues
modifica indica que se puede cambiar el valor del parámetro, no su tipo.
3
Ejercicios de Secuencias
Ejercicio 2. Tipar las siguientes secuencias:
i) [0 10 ,0 20 ,0 30 ,0 40 ] : [Char]
ii) [[6], [123 + 1, −59], [ ]] : [[Z]]
iii) [[0 u0 ], [0 b0 ,0 a0 ], [21]] : no tipa, mal formada.
iv) [[ ], [[ ]]] : [[[T ]]]
Facultad de Ciencias Exactas y Naturales
1
Universidad de Buenos Aires
Algoritmos y Estructuras de Datos I
Clase Práctica 2: Especificación - 22/08/14 (Tarde)
Observación: La lista vacı́a es un elemento válido de tipo lista pero que no contiene ningún elemento,
es decir su longitud es 0. Si no se puede deducir del contexto, su tipo es T (potencialmente cualquier
tipo del lenguaje).
Ejercicio 3. Dar las secuencias:
i) Definir la lista de los primeros 10 múltiplos de 5
[5 × x|x ← [1..11)]
ii) Idem anterior pero sólo impares
[5 × x | x ← [1..10], x mod 2 == 1]
iii) Dar por extensión la siguiente lista: [y | y ← [1..6], x ← [1..8], x < y ∧ y ≤ 3]
[2, 3, 3]
Observación: Notar cómo recorren los selectores: de izquierda a derecha, por cada valor que toma
uno se fija y se empieza a recorrer desde el principio el que haya a derecha, ası́ sucesivamente. En el
último ejemplo, para el valor 1 en y se recorre x de 1 a 8, luego para 2 en y se recorre x de 1 a 8, y
ası́. En las secuencias por compresión, las variables ligadas son aquellas que se definen en los selectores,
mientras que las libres son las que se utilizan porque vienen del contexto donde se está utilizando la lista
(parámetros de entrada de problema, función aux, de salida).
4
Ejercicios de Funciones Auxiliares
Notar que las auxiliares no llevan precondiciones. Si se definen fuera de una especificación pueden
utilizarse en otros problemas, por lo tanto se recomienda fuertemente que nunca se indefinan y sean
válidas para todo el dominio de los parámetros que reciben. En ese caso también será útil que su nombre
sea declarativo y que no estén atadas al contexto de un problema particular.
Ejercicio 4. Sea a una secuencia, obtener su secuencia inversa.
aux inversa(a : [T ]) : [T ] = [a|a|−i−1 | i ← [0.. |a|)]
Observación: ¿Podrı́a haber hecho una especificación para este problema? Sı́, todo depende del
propósito que se busque. Si lo queremos utilizar en otros problemas, y como no necesita ninguna precondición, es útil tenerlo como auxiliar. ¿Está mas cerca del cómo o del qué? Podrı́a tener una especificación del problema que para cada posición del arreglo dijera dónde queda en el resultado (quizás
engorroso indices, necesitamos cuantificadores).
Ejercicio 5. Sea a una secuencia, obtener la subsecuencia que va de d hasta h.
aux subsec(a : [Z], d, h : Z) : [Z] = [ai | i ← [0.. |a|), d ≤ i ≤ h]
Observación: ¿Hace falta esta auxiliar? Ya contamos con la operación de listas subsec, que como
en este caso cuando alguno de los indices no está en rango o no respeta el orden da [ ].
Ejercicio 6. Escribir una función auxiliar que, dada una secuencia s, devuelva la cantidad de elementos
pares en la misma.
aux cantP ares(s : [Z]) : [Z] = cuenta(True, [x mod 2 == 0 | x ← s])
aux cantP ares(s : [Z]) : [Z] = long([x | x ← s, x mod 2 == 0])
Ejercicio 7. Escribir una función auxiliar que, dada una secuencia s, determine si hay un elemento par
en la misma.
aux hayP ares(s : Z) : Bool = cantP ares(s) > 0
aux hayP ares(s : Z) : Bool = (∃x ← s) x mod 2 == 0
aux hayP ares(s : Z) : Bool = alguno([x mod 2 == 0 | x ← s])
Observación: ¿Cuál se acerca más al qué de las resoluciones anteriores? Destacar la segunda versión
como un exponente sencillo de lo que se busca con la especificación (definir el qué).
Facultad de Ciencias Exactas y Naturales
2
Universidad de Buenos Aires
Algoritmos y Estructuras de Datos I
Clase Práctica 2: Especificación - 22/08/14 (Tarde)
Ejercicio 8. Determinar si una secuencia s es prefijo de otra t (ejemplo: [1, 5] es prefijo de los números
de celular, por ejemplo de [1, 5, 5, 0, 0, 0, 0, 0, 1, 2] o [0 h0 ,0 i0 ,0 p0 ,0 o0 ] de [0 h0 ,0 i0 ,0 p0 ,0 o0 ,0 c0 ,0 a0 ,0 m0 ,0 p0 ,0 o0 ]).
aux esP ref ijo(s, t : [T ]) : Bool = |s| ≤ |t| ∧ (∀i ← [0.. |s|)) ti == si
aux esP ref ijo(s, t : [T ]) : Bool = |s| ≤ |t| ∧ t[0..|s|) == s
Observación: Notar que si bien no contamos con requiere, en auxiliares de tipo Bool podemos hacer
uso de la semántica de cortocircuito para evitar indefiniciones.
Ejercicio 9. Determinar si todos los elementos de una secuencia son distintos.
aux distintos(s : [T ]) : Bool = (∀i, j ∈ [0.. |s|), i 6= j) si 6= sj
Observación: ¿Vale para la secuencia vacı́a? Sı́, pues ver si se cumple una condición para todos los
elementos de esa lista, es decir ninguno, es verdadero. Recordar todos([ ]) es true.
5
Ejercicios de Especificación lvl 2
Ejercicio 10. Dada una secuencia de enteros s, modificarla de manera tal que al comienzo queden sus
elementos pares y luego sus impares.
i) La siguiente es una solución incorrecta. ¿Por qué?
problema separar(s : [Z]){
modifica s;
asegura : s == dameP ares(pre(s)) + +dameImpares(pre(s));
aux dameP ares(s : [Z]) : [Z] = [x | x ← s, x mod 2 == 0];
aux dameImpares(s : [Z]) : [Z] = [x | x ← s, x mod 2 == 1];
}
El problema radica en que por cómo se arman las listas por comprensión, se impone un orden
sobre los elementos resultantes, dejando afuera soluciones igualmente válidas (probar qué ejemplos
cumplen con la poscondición y cuáles no). En otras palabras, se está sobreespecificando.
ii) Solución correcta posible:
problema separar(s : [Z]){
modifica s;
asegura : mismos(s[0..cantP ares(pre(s)) , dameP ares(pre(s));
asegura : mismos(s[cantP ares(pre(s))..|(s)|) , dameImpares(pre(s));
aux dameP ares(s : [Z]) : [Z] = [x | x ← s, x mod 2 == 0];
aux dameImpares(s : [Z]) : [Z] = [x | x ← s, x mod 2 == 1];
}
Utilizamos auxiliares que están definidas fuera de la especificación. En el caso de mismos es una
auxiliar dada en el lenguaje, de tipo Bool y que evalúa que dos secuencias tengan los mismos
elementos, sin importar el orden.
Ejercicio 11. Dado un número entero n, obtener la lista de todos los números naturales que lo dividen
(en cualquier orden).
i) La siguiente solución es incorrecta según lo que se pide, ¿por qué?
problema obtenerDivisores(n : Z) = result : [Z]{
requiere : n > 0;
asegura : (∀d ← result) n mod d == 0;
}
Es importante pensar en qué resultados queremos aceptar al especificar. En este caso, si n es 30,
result == [2, 3] cumple con la poscondición. Sin embargo no es solución al problema inicial, puesto
que faltan divisores. Cuando permitimos más soluciones al problema de lo que buscamos, estamos
subespecificando.
Facultad de Ciencias Exactas y Naturales
3
Universidad de Buenos Aires
Algoritmos y Estructuras de Datos I
Clase Práctica 2: Especificación - 22/08/14 (Tarde)
ii) Especificación correcta posible:
problema obtenerDivisores(n : Z) = result : [Z]{
requiere : n > 0;
asegura soloHayDivisores: (∀d ← result) n mod d == 0);
asegura estanTodosLosDivisores: (∀d ← [1..n], n mod d == 0) d ∈ result;
}
Observación: En definitiva lo que querı́amos modelar era d ∈ result ↔ d divide a n, pero en la
primera solución sólo estabamos asegurando →. Notar que esta manera de especificar, utilizando
los existenciales, nos permite dar una descripción de qué se espera devolver, evitando problemas de
orden en la secuencia. Además, podrı́amos sintetizar los dos asegura en uno ¿cómo? (mismos).
6
Comentario
Es necesario remarcar que hay muchas maneras de especificar, y que dentro de la correctitud objetiva
existe un grado de subjetividad para elegir qué camino tomar. En ese sentido, estas posibles soluciones
pretenden servir como guı́a para explorar resoluciones posibles, pero no deben interpretarse como las
únicas correctas.
Facultad de Ciencias Exactas y Naturales
4
Universidad de Buenos Aires
Descargar