Especificación Clase 3 - Departamento de Computación

Anuncio
Lenguaje de especificación
Algoritmos y Estructuras de Datos I
I
Sirve para especificar problemas
I
Es un lenguaje “tipado”: los elementos pertenecen a distintos
dominios o conjuntos (enteros, reales, tipos definidos por
nosotros, por ustedes, etc.)
I
Incluye a la lógica proposicional trivaluada con el tipo Bool
I
Incluye tipos compuestos
I
Un tipo compuesto (central) es la secuencia.
Primer cuatrimestre de 2012
Departamento de Computación - FCEyN - UBA
Especificación - clase 3
Lenguaje de especificación
1
Especificar problemas versus definir funciones auxiliares
2
Auxiliares
La especificación de un problema
asignan un nombre a una expresión e
aux f (argumentos) : tipo = e;
I
es un contrato que debe cumplir un algoritmo para ser
solución del problema.
I
no puede incluir la especificación de otro problema.
los argumentos son opcionales; si están instancian variables de e.
I
sı́ puede incluı́r funciones auxiliares ya definidas.
tipo es el tipo del resultado de la expresión e.
f es el nombre.
e es la expresión nombrada por f .
La definición de una función auxiliar
I
f puede usarse en la especificación en vez de la expresión e.
es un nombre para una expresiones del lenguaje de
especificación de un cierto tipo
I
sirve como abreviatura, o para aumentar legilibildad
I
se usa adentro de una o más especifcaciones
I
puede estar basada en una o más funciones auxiliares.
Ejemplo
aux suc(x : Int) : Int = x + 1;
Se puede usar, por ejemplo, en la postcondición de un problema.
Las expresiones auxiliares facilitan la lectura y la escritura de
especificaciones. Funcionan como abreviaturas.
3
4
Tipos de datos
Tipo Bool (valor de verdad)
Sus valores son: 1, 0 , −
I constantes: true, false, ⊥ (o Indef)
I conectivos lógicos: ¬, ∧, ∨, →, ↔ con la semántica
secuencial trivaluada que vimos antes
Un tipo de datos es un conjunto de valores con operaciones
asociadas.
I
tipos básicos: Z, R, Bool, Char
(con sus constantes, y sus operaciones)
I
I
I
I
tipos compuestos
I
para hablar de un elemento de un tipo T en nuestro lenguaje
escribimos un término (o expresión)
I
I
I
I
I
I
I
variable de tipo T
constante de tipo T
función aplicada a otros términos (del tipo T o de otro tipo)
comparación: A == B
I
I
todos los tipos tienen un elemento distinguido: ⊥ o Indef.
I
I
I
¬A se puede escribir no(A)
(A ∧ B) se puede escribir (A && B)
(A ∨ B) se puede escribir (A || B)
(A → B) se puede escribir (A --> B)
(A ↔ B) se puede escribir (A <--> B)
todos los tipos tienen esta operación
(A y B deben ser del mismo tipo T )
es de tipo bool
es verdadero si el valor de A es igual al valor de B
(salvo que alguno esté indefinido - ver hoja 11)
A 6= B o A ! = B es equivalente a ¬(A == B)
semántica secuencial trivaluada
6
5
Tipo Z (o se puede escribir Int)
Tipo R (o se puede escribir Float)
Sus elementos son los números enteros
I
I
constantes: 0 ; 1 ;
operaciones aritméticas
I
I
I
I
I
I
I
I
;
2
;
−2
;
...
Sus elementos son los números reales.
I
a + b (suma)
a − b (resta)
a ∗ b (multiplicación)
a div b (división entera)
a mod b (resto de dividir a a por b)
ab o pot(a,b) (potencia)
abs(a) (valor absoluto)
I
I
I
I
I
I
I
I
si A es verdadero
si A es falso
si A es indefinido
7
;
81
;
7,4552
;
π...
las mismas que Int, salvo div y mod
a/b (división)
logb (a) (logaritmo)
trigonométricas
las mismas que para Int
conversión a entero
I
β o beta. Si A es de tipo Bool,
−7
comparaciones (de tipo bool)
I
a < b (menor); a ≤ b o a <= b (menor o igual)
a > b (mayor); a ≥ b o a >= b (mayor o igual)


1
β(A) = beta(A) = 0


−
constantes: 0 ; 1 ;
operaciones aritméticas
I
comparaciones (de tipo bool)
I
I
−1
bac o int(a)
todos los términos de tipo Int pueden usarse como términos
de tipo Float
8
Tipo Char (caracteres)
Términos
Sus elementos son los las letras, dı́gitos y sı́mbolos
I
I
constantes:
‘a‘, ‘b‘, ‘c‘, . . . , ‘z‘, . . . , ‘A‘, ‘B‘, ‘C ‘, . . . , ‘Z ‘, . . . , ‘0‘, ‘1‘, ‘2‘, . . . , ‘9‘
(en algún orden)
I
numera todos los caracteres
no importa mucho cuál es el valor de cada uno, pero
I
I
I
I
constantes del tipo
I
combinaciones de funciones aplicadas a funciones, constantes
y variables
Ejemplos de términos de tipo Int
ord(‘a‘) + 1 == ord(‘b‘)
ord(‘A‘) + 1 == ord(‘B‘)
ord(‘1‘) + 1 == ord(‘2‘)
función char
I
I
variables del tipo o
función ord
I
I
I
es la inversa de ord
I
0+1
I
((3 + 4) ∗ 7)2 − 1
I
2 ∗ β(1 + 1 == 2)
I
1 + ord(‘A‘)
con x variable de tipo Int; y de tipo Float; z de tipo Bool
I
las comparaciones entre caracteres son comparaciones entre
sus órdenes
I
I
I
I
a < b es equivalente a ord(a) < ord(b)
lo mismo para ≤, >, ≥
I
2∗x +1
β(y 2 > π) + x
(x mod 3) ∗ β(z)
9
Semántica de los términos
10
Tipos enumerados
Los términos representan elementos de los tipos
I
Los términos tienen valor indefinido cuando no se puede hacer
alguna operación. Por ejemplo 1 div 0, o , (−1)1/2
tipo Nombre = constantes;
Las operaciones son estrictas, excepto para Bool: si uno de sus
argumentos es indefinido, el resultado también está indefinido.
Ejemplos
I
0 ∗ (−1)1/2 es indefinido (∗ es estricto)
I
01/0 es indefinido (pot es estricto)
I
((1 + 1 == 2) ∨ (0 > 1/0)) es verdadero (∨ no es estricto)
Cantidad finita de elementos.
Cada uno, denotado por una constante
Las comparaciones con ⊥ son indefinidas; en particular,
si x está indefinido, x == x es indefinido (no es verdadero)
11
I
Nombre (del tipo): tiene que ser nuevo
I
constantes: nombres nuevos separados por comas
I
Convención: todos con mayúsculas
I
ord(a) da la posición del elemento en la definición
(empezando de 0)
I
Inversa: ord−1
12
Tipos enumerados. Ejemplos
Tipo upla
El tipo Bool es básico; está predefinido ası́:
Definimos el tipo Dı́a ası́:
Uplas, de dos o más elementos, de cualquier tipo.
(T1 , T2 , ...Tn ): Tipo de las n-uplas de elementos de tipo
T1 , T2 , ... Tn , donde n es fijo. El tipo upla es
tipo Dı́a = Lunes, Martes, Miércoles, Jueves, Viernes, Sábado, Domingo;
primitivo del lenguaje de especificación.
tipo Bool = 0, 1, -;
I
I
I
I
I
I
Ejemplos
Valen
(Int, Int) son los pares ordenados de enteros.
ord(Lunes) == 0
Dı́a(2) == Miércoles
Jueves < Viernes
(Int, Char, Bool) son la triplas ordenadas con un entero, luego un
carácter y luego un valor booleano.
Podemos definir
prm, sgd, 3esimo, nesimo dan sus componentes.
aux esFinde(d : Dı́a) : Bool = (d == Sábado || d == Domingo);
Ejemplo
prm((7, 5)) == 7
Otra forma
aux esFinde2(d : Dı́a) : Bool = d > Viernes;
13
Secuencias ( o listas)
14
Secuencias. Notación
La secuencia vacı́a (de elementos de cualquier tipo) se escribe [ ]
Una secuencias de elementos de tipo T es una sucesión finita de
elementos de tipo T (importa el órden).
Una forma de escribir un elemento de tipo secuencia de tipo T es
escribir dentro de un juego de corchetes cada uno de los elementos
de tipo T separados por comas.
T es un tipo arbitrario
Ejemplos
[T ] es el tipo de las secuencias cuyos elementos son de tipo T
una secuencia de Int
Hay secuencias de Int, de Bool, de Dı́as, de 5-uplas;
[1, 1 + 1, 3, 2 ∗ 2, 3, 5]
también hay secuencias de secuencias de tipo T .
una secuencia de secuencias de Int
etcétera
[[12, 13], [-3, 9, 0], [5], [], [], [3]]
15
16
Secuencias por comprensión
Secuencias por comprensión. Ejemplos
[expresión | selectores, condiciones]
[expresión | selectores, condiciones]
expresión: cualquier expresión válida del lenguaje
[x | x ∈ [1, 2, 3, 4, 5, 6, 7, 8, 9], x
selectores: tienen la forma variable ∈ secuencia, donde variable se
instancia en cada uno de los elementos de la secuencia.
mód 2 == 0] == [2, 4, 6, 8]
condiciones: son expresiones de tipo Bool sobre una o más variable.
En caso de que no haya condiciones, se considera siempre verdadero.
[ [x] | x ∈ [ ‘a‘,‘l‘,‘g‘,‘o‘,‘r‘,‘i‘ ,‘t‘, ‘m‘, ‘o‘, ‘s‘, ‘ ‘, ‘I‘] ] ==
[ [ ‘a‘ ], [ ‘l‘ ], [ ‘g‘ ], [ ‘o‘ ], [ ‘r‘ ], [ ‘i‘ ], [ ‘t‘ ], [ ‘m‘ ], [ ‘o‘ ], [ ‘s‘ ], [ ‘ ‘ ], [ ‘I‘ ] ]
Esta notación da una nueva secuencia que contiene, uno tras otro, los
valores de la expresion calculados a partir de los elementos indicados por
los selectores que cumplen las condiciones.
[(x, y ) | x ∈ [1, 2], y ∈ [1, 2, 3], x < y ] == [(1, 2), (1, 3), (2, 3)]
A las variables dentro de los selectroes se las llama ligadas.
A las demás se las llama libres.
17
Secuencias por comprensión, varios selectores
18
Secuencias dadas por intervalos
[expresión1 ..expresión2 ]
[(x, y )|x ← a, y ← b, x =/= y ]
Las expresiones tienen que ser del mismo tipo, discreto y
totalmente ordenado (por ejemplo, Int, Char, enumerados)
es distinto de
La secuencia tiene todos los valores del tipo entre expresión1 y
expresión2 (ambos inclusive)
[(x, y )|y ← b, x ← a, x =/= y ]
Sı́, el orden importa...
Si no vale expresión1 ≤ expresión2 , la secuencia es vacı́a
Con un paréntesis en lugar de un corchete, se excluye uno de los
extremos o ambos
[(x, y )|x ← [1, 2], y ← [3, 4]] == [(1, 3), (1, 4), (2, 3), (2, 4)]
Ejemplos:
I
[(x, y )|y ← [3, 4], x ← [1, 2]] == [(1, 3), (2, 3), (1, 4), (2, 4)]
I
I
I
19
[5..9] == [5, 6, 7, 8, 9]
[5..9) == [5, 6, 7, 8]
(5..9] == [6, 7, 8, 9]
(5..9) == [6, 7, 8]
20
Ejemplos de secuencias por comprensión
Ejemplos de secuencias por comprensión y auxiliares
Divisores comunes (asumir a y b positivos)
aux divCom(a, b : Int) : [Int] =
Cuadrados de los elementos impares
[x|x ← [1..a + b], divide(x, a), divide(x, b)];
aux cuadImp(a : [Int]) : [Int] = [x ∗ x|x ← a, ¬divide(2, x)];
aux divide(a, b : Int) : Bool = b mod a == 0;
Ejemplo:
Ejemplo:
cuadImp([1..9)) == [1, 9, 25, 49]
divCom(8, 12) == [1, 2, 4]
¿Cuáles son variables libres y cuáles ligadas?
21
Ejemplos de secuencias por comprensión y auxiliares
22
Operaciones con secuencias
I
Longitud: long (a : [T ]) : Int
I
I
Suma de los elementos distintos
I
aux sumDist(a, b : [Int]) : [Int] = [x + y | x ← a, y ← b, x 6= y ];
Indexación: indice(a : [T ], i : Int) : T
I
I
Ejemplo:
I
I
sumDist([1, 2, 3], [2, 3, 4, 5]) == [3, 4, 5, 6, 5, 6, 7, 5, 7, 8]
I
requiere 0 ≤ i < |a|;
Elemento en la i-ésima posición de a
La primera posición es la 0
Notación: indice(a, i) se puede escribir a[i] y también ai
Cabeza: cab(a : [T ]) : T
I
I
23
Longitud de la secuencia a
Notación: long (a) se puede escribir |a|
requiere |a| > 0;
Primer elemento de la secuencia
24
Más operaciones con secuencias
I
Cola: cola(a : [T ]) : [T ]
I
I
I
y más operaciones con secuencias
I
requiere |a| > 0;
La secuencia sin su primer elemento
I
I
Pertenencia: en(t : T , a : [T ]) : Bool
I
I
I
I
Indica si el elemento aparece (al menos una vez) en la
secuencia
Notación: en(t, a) se puede escribir t en a y también t ∈ a
t∈
/ a es ¬(t ∈ a)
I
Agregar cabeza: cons(t : T , a : [T ]) : [T ]
I
I
Sublista de a en las posiciones entre d y h (ambas inclusive)
Cuando no es 0 ≤ d ≤ h < |a|, da la secuencia vacı́a
otra secuencia cambiando el elemento que hay en una
posición: cambiar (a : [T ], i : Int, val : T ) : [T ]
I
Una secuencia como a, agregándole t como primer elemento
Notación: cons(t, a) se puede escribir t : a
Secuencia con los elementos de a, seguidos de los de b
Notación: conc(a, b) se puede escribir a + +b
Subsecuencia: sub(a : [T ], d, h : Int) : [T ]
I
I
I
Concatenación: conc(a, b : [T ]) : [T ]
I
requiere 0 ≤ i < |a|;
Igual a la secuencia a, pero el valor en la posición i es val
25
Subsecuencias con intervalos
26
Operaciones sobre secuencias que dan números
I
I
Sumatoria: sum(sec : [T ]) : T
I
Notación para obtener la subsecuencia de una secuencia dada
que está en un intervalo de posiciones
I
I
I
I
Admite intervalos abiertos
I
I
I
I
I
I
I
a[d..h] == sub(a, d, h)
a[d..h) == sub(a, d, h − 1)
a(d..h] == sub(a, d + 1, h)
a(d..h) == sub(a, d + 1, h − 1)
a[d..] == sub(a, d, |a| − 1)
a(d..] == sub(a, d + 1, |a| − 1)
I
Productoria: prod(sec : [T ]) : T
I
I
I
I
27
T debe ser un tipo numérico, es decir, Float o Int
Calcula la suma de todos los elementos de la secuencia
Si sec es vacı́a, el resultado es 0
P
Notación: sum(sec) se puede escribir
sec
Ejemplo:
P
aux potNegDosHasta(n : Int) : Float = [2−m | m ← [1..n]];
T debe ser un tipo numérico (Float, Int)
Calcula el producto de todos los elementos de la secuencia
Si sec es vacı́a, el resultado es 1
Q
Notación: prod(sec) se puede escribir sec
28
Operaciones con secuencias que dan un valor booleano
Variante notacional de todos( )
(∀ selectores, condiciones) expresión
I
todos(sec : [Bool]) : Bool
I
Es verdadero solamente si todos los elementos de la secuencia son
verdaderos (o la secuencia es vacı́a)
I
I
alguno(sec : [Bool]) : Bool
Término de tipo Bool
Afirma que todos los elementos de una lista por comprensión
cumplen una propiedad
Equivale a escribir todos([expresión | selectores, condiciones])
Ejemplo: “todos los elementos en posiciones pares son
mayores que 5”:
auxpar (n : Int) : Bool = n mod 2 == 0;
Es verdadero solamente si algún elemento de la secuencia es
verdadero (y ninguno es Indef)
aux posParM5(a : [Float]) : Bool =
(∀i ← [0..|a|), par (i)) a[i] > 5;
Esta expresión es equivalente a
todos([a[i] > 5|i ← [0..|a|), par (i)]);
29
Variante notacional de alguno( )
30
Cantidad de ocurrencias en una secuencia
Para contar cuántos elementos de una secuencia cumplen una condición
medimos la longitud de una secuencia definida por comprensión
(∃ selectores, condiciones)expresión
I
Hay algún elemento que cumple la propiedad
Ejemplos:
¿Cuántas veces aparece el elemento x en la secuencia a?
I
Equivale a alguno([expresión | selectores, condiciones])
aux cuenta(x : T , a : [T ]) : Int = long ([y |y ← a, y == x]);
I
Notación: en lugar de ∃ se puede escribir existe o existen
Podemos usarla para saber si dos secuencias tienen los mismos elementos
(quizás en otro orden)
Ejemplo:
“Hay algún elemento de la lista que es par y mayor que 5”:
aux mismos(a, b : [T ]) : Bool =
(|a| == |b| ∧ (∀c ← a) cuenta(c, a) == cuenta(c, b));
aux hayParM5(a : [Int]) : Bool = (∃x ← a, par (x)) x > 5;
¿Cuántos primos positivos hay que sean menores a n?
Es equivalente a
aux primosMenores(n : Int) : Int = long ([y | y ← [0..n), primo(y )]);
aux primo(n : Int) : Bool =
(n ≥ 2 ∧ ¬(∃m ← [2..n)) n mod m == 0);
alguno([x > 5|x ← a, par (x)]);
31
32
Acumulación
Ejemplos de acumulación
Suma
aux sum(l : [Float]) : Float = acum(s + i | s : Float = 0, i ← l);
acum(expresión | inicializacion, selectores, condición)
Construye un valor a partir de una o más secuencias, acumulando valores
Productoria
inicializacion tiene la forma acumulador : tipoAcum = init
aux prod(l : [Float]) : Float = acum(p ∗ i | p : Float = 1, i ← l);
I
acumulador es un nombre de variable (nuevo) de tipo tipoAcum
I
init es una expresión de tipo tipoAcum
Fibonacci
aux fiboSuc(n : Int) : [Int] =
acum(f + +[f [i − 1] + f [i − 2]] | f : [Int] = [1, 1], i ← [2..n]);
expresión: es arbitraria, y puede (y suele) aparecer el acumulador
si n ≥ 1, da los primeros n + 1 números de Fibonacci
si n == 0, da [1, 1]
por ejemplo, fiboSuc(5) == [1, 1, 2, 3, 5, 8]
selectores y condición son como en las secuencias por comprensión
Importante: acumulador no puede aparecer en la condición
El n-ésimo número de Fibonacci (n ≥ 1)
El valor inicial de acumulador es el valor de init Por cada valor de los
selectores, tenemos un valor de expresión que se acumula en la variable
acumulador. El resultado es el valor final del acumulador.
aux fibo(n : Int) : Int = (fiboSuc(n − 1))[n − 1];
por ejemplo, fibo(6) == 8
34
33
Más ejemplos de acumulación
Ejemplos de especificación con listas y acumulación
Sumar los inversos multiplicativos de los reales en una lista
Concatenación de elementos de una secuencia de secuencias
problema sumarInvertidos(a : [Float]) = result : Float {
requiere 0 ∈
/ a; P
asegura result = [1/x | x ← a];
}
aux concat(a : [[T ]]) : [T ] =
acum(l + +c | l : [T ] = [], c ← a);
por ejemplo, concat([[1, 2], [3], [4, 5, 6]]) == [1, 2, 3, 4, 5, 6]
Precondición: que el argumento no contenga ningún 0
Si no, la poscondición podrı́a indefinirse.
Secuencias por comprensión
Formas equivalentes:
El término [expresión | selectores, condición] es equivalente a
acum(res + +[expresión] | res : [T ] = [], selectores, condición);
35
I
requiere (∀x ← a) x 6= 0;
I
requiere ¬(∃x ← a) x == 0;
I
requiere ¬(∃i ← [0..|a|)) a[i] == 0;
36
Un ejemplo de sobrespecificación usando secuencias
Otro ejemplo de sobrespecificación
Problema: dada una lista, retornar una lista con los mismos elementos
que no esté ordenada de menor a mayor.
Problema:
dar los ı́ndices de los numeros pares de una lista, en cualquier órden.
problema desordenar(a : [Int]) = res : [Int] {
requiere |a| > 1;
requiere not(todos([a[0] == a[i]|i ∈ [1..|a|)]));
asegura mismos(a, res);
asegura ordenado up(res);
aux ordenado up(`) :Bool = (∀i ← [0..|ell| − 1)) `[i] ≥ `[i + 1]];
}
está sobrespecificado porque impone el órden de mayor a menor.
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 órden de a.
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]);
}
Ası́ sı́ está bien:
problema desordenar(a : [Int]) = res : [Int] {
requiere |a| > 1;
requiere not(todos([a[0] == a[i]|i ∈ [1..|a|)]));
asegura mismos(a, res);
asegura not(ordenado up(res));
aux ordenado up(`) :Bool = (∀i ← [0..|ell| − 1)) `[i] ≥ `[i + 1]];
}
37
Especificación. Nombres para las condiciones
Problema: Dar el ı́ndice del menor elemento de una lista de enteros no
negativos y distintos entre sı́.
problema ı́ndiceMenorDistintos(a : [Float]) = 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;
}
Nombramos las precondiciones, para aclarar su significado
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
Otra forma de escribir la segunda precondición:
requiere Distintos2: (∀i ← [0..|a|)) a[i] ∈
/ a[0..i);
39
38
Descargar