Especificación, clase 4 - Departamento de Computación

Anuncio
Especificación. Nombres para las condiciones
Problema: Dar el ı́ndice del menor elemento de una lista de enteros no
negativos y distintos entre sı́.
Algoritmos y Estructuras de Datos I
problema ı́ndiceMenorDistintos(a : [Int]) = res : Int {
requiere NoNegativos: (∀x ← a) x ≥ 0;
requiere Distintos: (∀i ← [0..|a|), j ← [0..|a|), i 6= j) ai 6= aj ;
asegura 0 ≤ res < |a|;
asegura (∀x ← a) a[res] ≤ x;
}
Segundo cuatrimestre de 2013
Departamento de Computación - FCEyN - UBA
Nombramos las precondiciones, para aclarar su significado.
Especificación - clase 4
I
Los nombres también pueden usarse como predicados en cualquier
lugar de la especificación.
I
El nombre no alcanza, hay que escribir la precondición en el
lenguaje.
Tipos compuestos
Otra forma de escribir la segunda precondición:
requiere Distintos2: (∀i ← [0..|a|)) a[i] ∈
/ a[0..i);
1
Un ejemplo de sobrespecificación usando secuencias
2
Ası́ sı́ está bien
Problema: dada una lista, retornar una lista con los mismos elementos
que no esté ordenada de menor a mayor.
Problema: dada una lista, retornar una lista con los mismos elementos
que no esté ordenada de menor a mayor.
problema Desordena(a : [Int]) = res : [Int] {
requiere |a| > 1
requiere not(todos([a[i] == a[i + 1]|i ← [0..|a| − 1)]))
asegura mismos(a, res);
asegura (∀i ← [0..|res| − 1)) res[i] ≥ res[i + 1]];
aux cuenta(x : T , b : [T ]) : Int = long ([y |y ← b, y == x]);
aux mismos(a, b : [T ]) : Bool = (|a| == |b| ∧
(∀c ← a) cuenta(c, a) == cuenta(c, b));
}
está sobrespecificado porque impone el orden de mayor a menor.
problema Desordena(a : [Int]) = res : [Int] {
requiere |a| > 1
requiere not(todos([a[i] == a[i + 1]|i ← [0..|a| − 1)]))
asegura mismos(a, res);
asegura (∃i ← [0..|res| − 1)) res[i] > res[i + 1]];
aux cuenta(x : T , b : [T ]) : Int = long ([y |y ← b, y == x]);
aux mismos(a, b : [T ]) : Bool = (|a| == |b| ∧
(∀c ← a) cuenta(c, a) == cuenta(c, b));
}
3
4
Otro ejemplo de sobrespecificación
Tipos compuestos
I
cada valor de un tipo básico representa un elemento atómico,
indivisible. Vimos los tipos básicos Int, Float, Bool, Char; y también
los tipos enumerados.
I
un valor de un tipo compuesto contiene información que puede ser
dividida en componentes de otros tipos
I
ya vimos dos ejemplos de tipos compuestos:
Problema:
Dar los ı́ndices de los números pares de una lista, en cualquier orden.
problema ı́ndicesDePares(a : [Int]) = res : [Int]{
asegura 0 ≤ |res| ≤ |a|;
asegura res == [i|i ← [0..|a|), a[i]mod2 == 0];
}
sobrespecifica el problema, porque exige que res siga el orden de a.
I
I
Ası́ sı́ está bien:
problema ı́ndicesDePares(a : [Int]) = res : [Int]{
asegura 0 ≤ |res| ≤ |a|;
asegura mismos(res, [i|i ← [0..|a|), a[i]mod2 == 0]);
}
I
secuencias: un valor de tipo secuencia de enteros tiene varios
componentes, cada uno es un entero
tuplas: un valor de tipo par ordenado de enteros tiene dos
componentes
para definir un tipo compuesto, tenemos que darle
I
I
I
un nombre
uno o más observadores
un invariante de tipo
5
Tipos compuestos
6
Tipo Punto
Especifica un punto en el plano Componentes: coordenadas (x e y ).
Hay observador para obtener cada una.
I
Especificación de un tipo compuesto
nombre, observadores, invariante de tipo
I
Constructores de elementos de tipo compuesto
mediante la especificación de un problema.
Dan como resultado un elemento de tipo compuesto.
I
tipo Punto {
observador X (p : Punto) : Float;
observador Y (p : Punto) : Float
invariante True;
}
Especificamos un problema para la función que recibe dos números reales
y construye un punto con esas coordenadas.
Usamos los observadores para describir el resultado
Constructor
Funciones auxiliares que toman como argumentos elementos de un
tipo compuesto.
problema nuevoPunto(a, b : Float) = res : Punto {
asegura X (res) == a;
asegura Y (res) == b;
}
7
8
Un funcion auxiliar para el tipo compuesto Punto
Observadores
Un observador es una función que mapea elementos de un tipo compuesto
en los valores de sus componentes. Puede tener más parámetros.
Los observadores
I
función auxiliar para calcular la distancia entre dos puntos
1/2
aux dist(p, q : Punto) : Float = (X (p) − X (q))2 + (Y (p) − Y (q))2
Definen el tipo compuesto
I
I
Pueden tener precondiciones
I
I
si dos términos del tipo dan el mismo resultado para todos los
observadores, se los considera iguales. Ası́ queda definida la
igualdad == para tipos compuestos
(el == está en todos los tipos)
si vale la precondición, no pueden quedar indefinidos
No tienen postcondiciones propias
9
Tipo Cı́rculo
I
I
I
10
Invariantes de tipo
Especificación del tipo compuesto
tipo Cı́rculo {
observador Centro(c : Cı́rculo) : Punto;
observador Radio(c : Cı́rculo) : Float;
invariante Radio(c) > 0; }
Un invariante de tipo es una expresión booleana que relaciona los
observadores del tipo, y es verdadera para cada elemento del tipo.
Un invariante de tipo
Problema de construir un cı́rculo a partir de su centro y su radio.
Constructor
problema nuevoCı́rculo(c : Punto, r : Float) = res : Cı́rculo {
requiere r > 0;
asegura (Centro(res) == c ∧ Radio(res) == r ); }
Problema de construir un cı́rculo a partir de su centro y un punto
sobre la circunferencia.
Constructor
problema nuevoCı́rculoPuntos(c, x : Punto) = res : Cı́rculo {
requiere dist(c, x) > 0;
asegura (Centro(res) == c ∧ Radio(res) == dist(c, x)); }
I
Da condiciones que deben cumplir todos los elementos de un tipo
compuesto.
I
Al especificar un tipo damos explı́citamente los invariantes de tipo.
I
Al observar un elemento de un tipo compuesto está garantizado que
se cumplen los invariantes.
I
Al construir un elemento de un tipo compuesto tendremos que
asegurar que se cumplan los invariantes.
tipo T {
observador . . . ;
..
.
invariante R(x); }
11
12
Tipo MatrizhT i
Tipos genéricos o tipos paramétricos
I
I
ejemplo: una instancia del tipo genérico MatrizhT i es MatrizhChari
I el tipo de matrices cuyos elementos son caracteres
I
un tipo genérico define una familia (infinita) de tipos
I algunos miembros de la familia MatrizhT i son
MatrizhInti , MatrizhPuntoi , MatrizhMatrizhFloatii
cada elemento de la familia es una instancia del tipo genérico
(los problemas que usan un tipo genérico definen una familia
de problemas)
el nombre del tipo tiene parámetros
I
I
I
tipo genérico de las matrices cuyos elementos pertenecen a un tipo
cualquiera T
tipo MatrizhT i{
observador filas(m : MatrizhT i) : Int;
observador columnas(m : MatrizhT i) : Int;
observador val(m : MatrizhT i, f , c : Int) : T {

requiere 0 ≤ f < filas(m);

requiere 0 ≤ c < columnas(m);
precondición del observador

}
invariante filas(m) > 0;
invariante columnas(m) > 0;
}
definen una familia de tipos
I
I
I
variables que representan a los tipos de los componentes
los parámetros de tipo se escriben entre los sı́mbolos h y i
después del nombre del tipo
los usamos para definir funciones auxiliares o especificar problemas
13
Operaciones genéricas con matrices
I
14
Operaciones sobre matrices para tipos instanciados
expresión que cuenta la cantidad de elementos de una matriz
I
aux elementos(m : MatrizhT i) : Int = filas(m) ∗ columnas(m);
I
expresión que suma los elementos de una matriz de enteros
aux suma(m : MatrizhInti) : Int =
P
[val(m, i, j) | i ← [0..filas(m)), j ← [0..columnas(m))];
especificación del problema de cambiar el valor de una posición de
una matriz
I
problema cambiar(m : MatrizhT i, f , c : Int, v : T ) {
requiere 0 ≤ f < filas(m);
requiere 0 ≤ c < columnas(m);
modifica m;
asegura filas(m) == filas(pre(m));
asegura columnas(m) == columnas(pre(m));
asegura val(m, f , c) == v ;
asegura (∀i ← [0..filas(m)))
(∀j ← [0..columnas(m)), ¬(i == f ∧ j == c))
val(m, i, j) == val(pre(m), i, j);
}
especificación del problema de construir la matriz identidad de n × n:
problema matId(n : Int) = ident : MatrizhInti {
requiere n > 0;
asegura filas(ident) == columnas(ident) == n;
asegura (∀i ← [0..n)) val(ident, i, i) == 1;
asegura (∀i ← [0..n))(∀j ← [0..n), i 6= j) val(ident, i, j) == 0;
}
I
I
I
15
¿importa el orden?
sı́; si estuviera al revés, se podrı́a indefinir val(ident, i, i)
en este caso, se indefinirı́a la poscondición, valiendo la
precondición
16
El tipo SecuenciahT i
I
ya lo usamos con su nombre alternativo: [T ]
I
presentamos también sus observadores: longitud e indexación
Operaciones genéricas con secuencias
I
aux ssc(a, b : [T ]) : Bool = (∃i ← [0..|b| − |a|] a == b[i..(i + |a|));
I
aux cab(a : [T ]) : T = a[0];
I
aux cola(a : [T ]) : [T ] = a[1..|a|);
I
aux en(t : T , a : [T ]) : Bool = [x | x ← a, x == t] 6= [ ];
}
I
aux sub(a : [T ], d, h : Int) : [T ] = [a[i] | i ← [d..h]];
invariante long (s : SecuenciahT i) ≥ 0
I
aux todos(sec : [Bool]) : Bool = False ∈
/ sec;
I
aux alguno(sec : [Bool]) : Bool = True ∈ sec;
tipo SecuenciahT i{
observador long (s : SecuenciahT i) : Int;
observador ı́ndice(s : SecuenciahT i, i : Int) : T {
requiere 0 ≤ i < long (s); precondición de observador
}
I
notaciones alternativas
I
I
|s| para la longitud
si o s[i] para la indexación
18
17
Secuencias de Char
Un ejemplo con secuencias de Char: palabras y textos
Indistintamente las llamamos
I
SecuenciahChari
I
[Char]
I
String
I
para representar un texto usamos el tipo SecuenciahChari
I
para representar una lista de palabras usamos el tipo
SecuenciahSecuenciahCharii
I
I
Problema: decidir si alguna palabra de una lista aparece en un libro
Precondiciones
I
I
19
son secuencias que en cada posición tienen una secuencia de
caracteres
cada palabra debe tener al menos una letra
no contienen ningún espacio (dejarı́an de ser palabras)
20
Un ejemplo con secuencias de Char: palabras y textos
Qué especifica este problema?
problema hayAlguna(palabras : [[Char]], libro : [Char]) = res : Bool{
requiere NoVacı́as : (∀p ← palabras) |p| > 0;
requiere SinEspacios : ¬(∃p ← palabras) ‘ ‘ ∈ p;
asegura res == (∃p ← palabras) ssc(p, libro);
}
problema ???(palabras : [[Char]], libro : [Char]) = res :???{
requiere NoVacı́as : (∀p ← palabras) |p| > 0;
requiere SinEspacios
P : ¬(∃p ← palabras) ‘ ‘ ∈ p;
asegura res ==
([β(ssc(p, libro)) | p ← palabras])
}
Que las palabras contengan solamente letras
requiere SinSı́mbolos: (∀p ← palabras) soloLetras(p);
aux letras: [Char ] = [‘A‘..‘Z ‘] + +[‘a‘..‘z‘];
aux soloLetras(s : [Char]) : Bool = (∀l ← s) l ∈ letras;
22
21
ifThenElsehT i
Tuplas
I
ya las mencionamos
I
secuencias de tamaño fijo
I
cada elemento puede pertenecer a un tipo distinto
I
ejemplos: pares, ternas
Función que elige entre dos elementos del mismo tipo, según una
condición booleana (guarda)
si la guarda es verdadera, elige el primero
I
si no, elige el segundo
Por ejemplo
tipo ParhA, Bi{
observador prm(p : Par hA, Bi) : A;
observador sgd(p : Par hA, Bi) : B;
}
I
expresión que devuelve el máximo entre dos elementos:
aux máx(a, b : Int) : Int = ifThenElsehInti(a > b, a, b)
cuando los argumentos se deducen del contexto, se puede escribir
directamente
tipo TernahA, B, C i{
observador prm3(t : TernahA, B, C i) : A;
observador sgd3(t : TernahA, B, C i) : B;
observador trc3(t : TernahA, B, C i) : C ;
}
I
I
aux máx(a, b : Int) : Int = ifThenElse(a > b, a, b)
aux máx(a, b : Int) : Int = if a > b then a else b
I
o bien
expresión que dado x devuelve 1/x si x 6= 0 y 0 sino
aux unoSobre(x : Float) : Float = if x 6= 0 then 1/x else 0
|
{z
}
Notación: ParhA, Bi también se puede escribir (A, B)
TernahA, B, C i también se puede escribir (A, B, C )
no se indefine cuando x = 0
23
24
Más aplicaciones de IfThenElse
I
agregar un elemento como primer elemento de una lista
aux cons(x : T , a : [T ]) : [T ] =
[if i == −1 then x else a[i] | i ← [−1..|a|)];
I
concatenar dos listas
aux conc(a, b : [T ]) : [T ] =
[if i < |a| then a[i] else b[i − |a|] | i ← [0..|a| + |b|)];
I
otra lista casi idéntica
aux casi-identica(a : [T ], i : Int, v : T ) : [T ] =
[if i 6= j then a[i] else v | j ← [0..|a|)];
25
Descargar