Post on 02-Jan-2017
Algoritmos y Estructuras de Datos I
Departamento de Computacion, FCEyN, UBA
2◦ cuatrimestre de 2008
1
ContenidoEspecificacion
I clase 1, p. 3I clase 2, p. 28I clase 3, p. 48I clase 4, p. 78
Programacion funcionalI clase 1: p. 94I clase 2: p. 114I clase 3: p. 131I clase 4: p. 154I clase 5: p. 181
Programacion imperativaI clase 1: p. 205I clase 2: p. 225I clase 3: p. 247I clase 4: p. 260I clase 5: p. 272
2
EspecificacionClase 1
Introduccion a la especificacion de problemas
3
Cursada
I clases teoricasI Paula Zabala y Santiago Figueira
I clases practicasI Carlos Lopez Pombo, Pablo Turjanski y Martın Urtasun
I sitio web de la materia: www.dc.uba.ar/people/materias/algo1I regimen de aprobacion
I parcialesI 3 parcialesI 3 recuperatorios (al final de la cursada)
I trabajos practicosI 3 entregasI 3 recuperatorios (cada uno a continuacion)I grupos de 4 alumnos
I examen final (si lo dan en diciembre 2008 o febrero/marzo2009, cuenta la nota de la cursada)
4
Objetivos y contenidosI Objetivos:
I especificar problemasI describirlos de forma no ambigua
I escribir programas sencillosI tratamiento de secuencias
I razonar acerca de estos programasI demostrar matematicamente que un programa es correctoI vision abstracta del proceso de computacionI manejo simbolico y herramientas para demostrar
I Contenidos:I especificacion
I describir problemas en un lenguaje formal (preciso, claro,abstracto)
I programacion funcional (Haskell)I parecido al lenguaje matematicoI escribir de manera simple algoritmos y estructuras de datos
I programacion imperativa (C++)I paradigma mas difundidoI mas eficienteI se necesita para seguir la carrera
5
Especificacion, algoritmo, programa
1. especificacion = descripcion del problemaI ¿que problema tenemos?I en lenguaje formalI describe propiedades de la solucionI Dijkstra, Hoare (anos 70)
2. algoritmo = descripcion de la solucion (escrito para humanos)I ¿como resolvemos el problema?
3. programa = descripcion de la solucion (escrito para lacomputadora)
I tambien, ¿como resolvemos el problema?I usando un lenguaje de programacion
6
Problemas y solucionSi voy a escribir un programa, es porque hay un problema aresolver
I a veces, la descripcion es vaga o ambigua
I no siempre es claro que haya solucion
I no siempre involucra uno o mas programas de computacion
Ejemplos de problemas:I cerre el auto con las llaves adentro
I seguramente tiene solucion, sin programas
I quiero calcular la edad de una personaI tal vez podamos crear un programa
I necesitamos que la empresa reduzca sus gastos en un 10 %I puede o no tener solucion, que puede o no requerir programas
nuevos o existentes
I soy muy petisoI quizas no tenga solucion
7
Problema vs. solucionParece una diferencia trivial
I en los ejemplos, es claraI pero es facil confundirlos
Ejemplo:
I confundir el problema con la solucion:A: “tengo un problema: necesito un alambre”B: “no tengo”
I en realidad, ese no era el problema:A: “tengo un problema: cerre el auto con las llaves adentro”B: “toma la copia de la llave que me pediste que te guardaracuando compraste el auto”
I confundir el problema con la solucion trae nuevos problemasI es muy comun en computacion
I los programas de computadora casi nunca son la solucioncompleta
I a veces forman parte de la solucionI ¡y a veces ni siquiera se necesitan! 8
Etapas en el desarrollo de programas
1. especificacion: definicion precisa del problema
↓2. diseno: elegir una solucion y dividir el problema en partes
↓3. programacion: escribir algoritmos e implementarlos en algun
lenguaje
↓4. validacion: ver si el programa cumple con lo especificado
↓5. mantenimiento: corregir errores y adaptarlo a nuevos
requerimientos
E D P V M · · ·
9
1. EspecificacionI el planteo inicial del problema es vago y ambiguoI especificacion = descripcion clara y precisaI optimo: lenguaje formal, por ejemplo, el lenguaje de la logica matematica
Por ejemplo, el problema de calcular de edad de una persona parece bienplanteado, pero:
I ¿como recibo los datos? manualmente, archivo en disco, sensorI ¿cuales son los datos? fecha de nacimiento, tamano de la persona,
deterioro celularI ¿como devuelvo el resultado? pantalla, papel, voz alta, emailI ¿que forma van a tener? dıas, anos enteros, fraccion de anos
Una buena especificacion responde algunas:I Necesito una funcion que, dadas dos fechas en formato dd/mm/aaaa, me
devuelva la cantidad de dıas que hay entre ellas.
todavıa faltan los requerimientos no funcionalesI forma de entrada de parametros y de salida de resultados, tipo de
computadora, tiempo con el que se cuenta para programarlo, etc.I no son parte de la especificacion funcional y quedan para otras materias
10
2. Diseno
Etapa en la que se responde
I ¿varios programas o uno muy complejo?I ¿como dividirlo en partes, que porcion del problema resuelve
cada una?I ¿distintas partes en distintas maquinas?I estudio de las partes que lo componen
I (mini) especificacion de cada parteI un programador recibe una sola (o una por vez)
I ¿programas ya hechos con los que interactuar?
I lo van a ver en Algoritmos y Estructuras de Datos II y enIngenierıa del Software I
11
3. Programacion - Algoritmos
Pensar en la solucion al problema. Escribir un algoritmo:
I pasos precisos para llegar al resultado buscado de manera efectivaI primero tienen que estar definidos los pasos primitivos
Ejemplo de algoritmo para la especificacion
Necesito una funcion que, dadas dos fechas a y b en formatodd/mm/aaaa, me devuelva la cantidad de dıas que hay entre ellas.
1. restar el ano de b al ano de a2. multiplicar el resultado por 3653. sumarle la cantidad de dıas desde el 1◦ de enero del ano de b hasta el dıa b4. restarle la cantidad de dıas desde el 1◦ de enero del ano de a hasta el dıa a5. sumarle la cantidad de 29 de febrero que hubo en el perıodo6. devolver ese resultado, acompanado de la palabra “dıas”
¿Son todos primitivos? (3, 4, 5)
I si no, hay que dar algoritmos para realizarlos
12
Pasos primitivos
I problema: sumar dos numeros naturalesI algoritmos:
I voy sumando uno al primero y restando uno al segundo, hastaque llegue a cero
I sumo las unidades del primero a las del segundo, despues lasdecenas y ası (“llevandome uno” cuando hace falta)
I escribo el primero en una calculadora, aprieto +, escribo elsegundo, aprieto =
I los tres son validosI depende de que operaciones primitivas tenga
I sumar / restar unoI sumar dıgitos (y concatenar resultados)I apretar una tecla de una calculadora
13
3. Programacion - Programas
I traducir el algoritmo (escrito o idea) para que unacomputadora lo entienda
I lenguaje de programacionI vamos a empezar usando uno: HaskellI despues, C++
I hay muchos otrosI pueden ser mas adecuados para ciertas tareasI depende del algoritmo, de la interfaz, tiempo de ejecucion, tipo
de maquina, interoperabilidad, entrenamiento de losprogramadores, licencias, etc.
14
Representacion de los datos
I ya vimos que era importante en el ejemplo de calcular la edadde una persona (dıas, meses, anos, fracciones de ano)
I otro ejemplo:I contar la cantidad de alumnos de cada sexoI yo puedo darme cuenta a simple vista (a veces)I la computadora probablemente no. ¿Que dato conviene tener?
I fotoI foto del documentoI ADNI valor de la propiedad sexo para cada alumno
I estructuras de datosI necesarias para especificarI puede ser que haya que cambiarlas al programarI las van a estudiar a fondo en Algoritmos y Estructuras de
Datos II
15
4. ValidacionAsegurarse de que un programa cumple con la especificacion
I testingI probar el programa con muchos datos y ver si hace lo que tiene
que hacerI en general, para estar seguro de que anda bien, uno tendrıa
que probarlo para infinitos datos de entradaI si hay un error, con algo de suerte uno puede encontrarloI no es infalible (puede pasar el testing pero haber errores)I en Ingenierıa del Software I van a ver tecnicas para hacer
testingI verificacion formal
I demostrar matematicamente que un programa cumple con unaespecificacion
I es mas difıcil que hacer testingI una demostracion cubre infinitos valores de entrada a la vez
(abstraccion)I es infalible (si esta demostrado, el programa no tiene errores)I en esta materia van a estudiar como demostrar que programas
simples son correctos para una especificacion16
5. Mantenimiento
I tiempo despues, encontramos erroresI el programa no cumplıa la especificacionI la especificacion no describıa correctamente el problema
I o cambian los requerimientos
I puede hacerlo el mismo equipo u otroI justifica las etapas anteriores
I si se hicieron bien la especificacion, diseno, programacion yvalidacion, las modificaciones van a ser mas sencillas y menosfrecuentes
17
Especificacion
I objetivos:I antes de programar: entender el problemaI despues de programar: determinar si el programa es correcto
I testingI verificacion formalI derivacion (construyo el programa a partir de la especificacion)
I estrategia:I evitar pensar (todavıa) una solucion para el problemaI limitarse a describir cual es el problema a resolver
I que propiedades tiene que cumplir una solucion para resolverel problema
I buscamos el que y no el como
18
Instancias de un problema
I ejemplo de problema: arreglar una cafeteraI para solucionarlo, necesitamos mas datos
I ¿que clase de cafetera es?I ¿que defecto tiene?I ¿cuanto presupuesto tenemos?
I se llaman parametros del problemaI cada combinacion de valores de los parametros es una
instancia del problemaI una instancia: “arreglar una cafetera de filtro cuya jarra pierde
agua, gastando a lo sumo $30”
I los valores de los parametros se llaman argumentos
19
Problemas funcionales
I no podemos especificar formalmente cualquier problemaI el hambre en el mundoI la salud de Sandro
I simplificacion (para esta materia)I problemas que puedan solucionarse con una funcion
I parametros de entradaI un resultado para cada combinacion de valores de entrada
20
Tipos de datos
Cada parametro tiene un tipo de datos
I conjunto de valores para los que hay ciertas operacionesdefinidas
Por ejemplo:I parametros de tipo fecha
I valores: ternas de numeros enterosI operaciones: comparacion, obtener el ano,...
I parametros de tipo dineroI valores: numeros reales con dos decimalesI operaciones: suma, resta,...
21
Encabezado de un problemaIndica la forma que debe tener una solucion.
problema nombre(parametros) = nombreRes : tipoRes
I nombre: nombre que le damos al problemaI sera resuelto por una funcion con ese mismo nombre
I nombreRes: nombre que le damos al resultadoI tipoRes: tipo de datos del resultadoI parametros: lista que da el tipo y el nombre de cada uno
EjemploI problema resta(minuendo, sustraendo : Int) = res : IntI la funcion se llama restaI da un resultado que vamos a llamar res
I y es de tipo Int (los enteros)I depende de dos parametros: minuendo y sustraendo
I tambien son de tipo Int22
ContratosI la funcion que solucione el problema va a ser llamada o invocada por
un usuarioI puede ser el programador de otra funcion
I especificacion = contrato entre el programador de una funcion queresuelva el problema y el usuario de esa funcion
Por ejemplo:I problema: calcular la raız cuadrada de un realI solucion (funcion)
I va a tener un parametro realI va a calcular un resultado real
I para hacer el calculo, debe recibir un numero no negativoI compromiso para el usuario: no puede proveer numeros negativosI derecho para el programador de la funcion: puede suponer que el
argumento recibido no es negativoI el resultado va a ser la raız del numero
I compromiso del programador: debe calcular la raız, siempre y cuandohaya recibido un numero no negativo
I derecho del usuario: puede suponer que el resultado va a ser correcto23
Partes de una especificacion
Tiene 3 partes
1. encabezado (ya lo vimos)
2. precondicionI condicion sobre los argumentosI el programador da por ciertaI lo que requiere la funcion para hacer su tareaI por ejemplo: “el valor de entrada es un real no negativo”
3. poscondicionI condicion sobre el resultadoI debe ser cumplida por el programador, siempre y cuando el
usuario haya cumplido la precondicionI lo que la funcion asegura que se va a cumplir despues de
llamarla (si se cumplıa la precondicion)I por ejemplo: “la salida es la raız del valor de entrada”
24
El contrato dice:El programador va a hacer un programa P tal que si elusuario suministra datos que hacen verdadera laprecondicion, entonces P va a terminar en una cantidadfinita de pasos y va a devolver un valor que haceverdadera la poscondicion.
I si el usuario no cumple la precondicion y P se cuelga o nocumple la poscondicion...
I ¿el usuario tiene derecho a quejarse? No.I ¿se viola el contrato? No. El contrato preve este caso y dice
que P solo debe funcionar en caso de que el usuario cumplacon la precondicion
I si el usuario cumple la precondicion y P se cuelga o no cumplela poscondicion...
I ¿el usuario tiene derecho a quejarse? Sı.I ¿se viola el contrato? Sı. Es el unico caso en que se viola el
contrato. El programador no hizo lo que se habıacomprometido a hacer.
25
Lenguaje naturales y lenguajes formalesI lenguajes naturales
I idiomas (castellano)I mucho poder expresivo (emociones, deseos, suposiciones y demas)I con un costo (conocimiento del contexto, experiencias compartidas
ambiguedad e imprecision)I queremos evitarlo al especificar
I lenguajes formalesI limitan lo que se puede expresarI todas las suposiciones quedan explıcitasI relacion directa entre lo escrito (sintaxis) y su significado (semantica)I pueden tratarse formalmente
I sımbolos manipulables directamenteI seguridad de que las manipulaciones son validas tambien para el significado
I ejemplo: aritmeticaI lenguaje formal para los numeros y sus operacionesI resolvemos problemas simbolicamente sin pensar en los significados
numericos y llegamos a resultados correctosI en Teorıa de Lenguajes y Logica y Computabilidad van a estudiarlos en
profundidad26
Especificacion formal
I ya aprendimos a escribir encabezadosI tenemos que dar tambien
I la precondicion (lo que la funcion requiere)I la poscondicion (lo que asegura)
I usamos un lenguaje formal para describir la precondicion y laposcondicion
I ejemplo de problema: calcular la raız cuadrada de un numeroI lo llamamos rcuadI Float representa el conjunto R
problema rcuad(x : Float) = result : Float {requiere x ≥ 0;asegura result ∗ result == x ;
}
27
EspecificacionClase 2
Logica proposicional - tipos basicos
28
Logica proposicional - sintaxis
I sımbolos
true , false , ⊥ , ¬ , ∧ , ∨ , → , ↔ , ( , )
I variables proposicionales (infinitas)
p , q , r , . . .
I formulas1. true, false y ⊥ son formulas2. cualquier variable proposicional es una formula3. si A es una formula, ¬A es una formula4. si A1,A2, . . . ,An son formulas, (A1 ∧ A2 ∧ · · · ∧ An) es una
formula5. si A1,A2, . . . ,An son formulas, (A1 ∨ A2 ∨ · · · ∨ An) es una
formula6. si A y B son formulas, (A→ B) es una formula7. si A y B son formulas, (A↔ B) es una formula
29
Ejemplos
¿Cuales son formulas?
I p ∨ q no
I (p ∨ q) sı
I p ∨ q → r no
I (p ∨ q)→ r no
I ((p ∨ q)→ r) sı
I (p → q → r) no
30
Semantica clasica
I 2 valores de verdad posibles
1. verdadero (1)2. falso (0)
I interpretacion:
I true siempre vale 1I false siempre vale 0I ¬ se interpreta como “no”, se llama negacionI ∧ se interpreta como “y”, se llama conjuncionI ∨ se interpreta como “o”(no exclusivo), se llama disyuncionI → se interpreta como “si... entonces”, se llama implicacionI ↔ se interpreta como “si y solo si”, se llama doble implicacion
o equivalencia
31
Semantica clasica
Conociendo el valor de las variables proposicionales de unaformula, conocemos el valor de verdad de la formula
p ¬p
1 0
0 1
p q (p ∧ q)
1 1 1
1 0 0
0 1 0
0 0 0
p q (p ∨ q)
1 1 1
1 0 1
0 1 1
0 0 0
p q (p → q)
1 1 1
1 0 0
0 1 1
0 0 1
p q (p ↔ q)
1 1 1
1 0 0
0 1 0
0 0 1
32
Ejemplo: tabla de verdad para ((p ∧ q)→ r)
p q r (p ∧ q) ((p ∧ q)→ r)
1 1 1 1 1
1 1 0 1 0
1 0 1 0 1
1 0 0 0 1
0 1 1 0 1
0 1 0 0 1
0 0 1 0 1
0 0 0 0 1
33
Semantica trivaluada
I 3 valores de verdad posibles
1. verdadero (1)2. falso (0)3. indefinido (−)
I es la que vamos a usar en esta materiaI ¿por que?
I queremos especificar problemas que puedan resolverse con unalgoritmo
I puede ser que un algoritmo realice una operacion invalida
I dividir por ceroI raız cuadrada de un numero negativo
I necesitamos contemplar esta posibilidad en la especificacion
I interpretacion:
I true siempre vale 1I false siempre vale 0I ⊥ siempre vale −I se extienden las definiciones de ¬,∧,∨,→,↔
34
Semantica trivaluada (secuencial)Se llama secuencial porque
I los terminos se evaluan de izquierda a derecha
I la evaluacion termina cuando se puede deducir el valor deverdad, aunque el resto este indefinido
Extendemos la semantica de ¬, ∧, ∨
p ¬p
1 0
0 1
− −
p q (p ∧ q)
1 1 1
1 0 0
0 1 0
0 0 0
1 − −0 − 0
− 1 −− 0 −− − −
p q (p ∨ q)
1 1 1
1 0 1
0 1 1
0 0 0
1 − 1
0 − −− 1 −− 0 −− − −
35
Semantica trivaluada (secuencial)
Extendemos la semantica de →, ↔
p q (p → q)
1 1 1
1 0 0
0 1 1
0 0 1
1 − −0 − 1
− 1 −− 0 −− − −
p q (p ↔ q)
1 1 1
1 0 0
0 1 0
0 0 1
1 − −0 − −− 1 −− 0 −− − −
36
Dos conectivos bastan
I ¬ y ∨I (A ∧ B) es ¬(¬A ∨ ¬B)I (A→ B) es (¬A ∨ B)I true es (A ∨ ¬A)I false es ¬true
I ¬ y ∧I (A ∨ B) es ¬(¬A ∧ ¬B)I (A→ B) es ¬(A ∧ ¬B)I false es (A ∧ ¬A)I true es ¬false
I ¬ y →I (A ∨ B) es (¬A→ B)I (A ∧ B) es ¬(A→ ¬B)I true es (A→ A)I false es ¬true
37
Tautologıas, contradicciones y contingenciasI una formula es una tautologıa si siempre toma el valor 1 para
valores definidos de sus variables proposicionalesPor ejemplo, ((p ∧ q)→ p) es tautologıa:
p q (p ∧ q) ((p ∧ q)→ p)
1 1 1 1
1 0 0 1
0 1 0 1
0 0 0 1
I una formula es una contradiccion si siempre toma el valor 0para valores definidos de sus variables proposicionalesPor ejemplo, (p ∧ ¬p) es contradiccion:
p ¬p (p ∧ ¬p)
1 0 0
0 1 0
I una formula es una contingencia cuando no es ni tautologıa nicontradiccion
38
Relacion de fuerza
Decimos que A es mas fuerte que B cuando (A→ B) es tautologıa.
Tambien decimos que A fuerza a B o que B es mas debil que A.
Por ejemplo,
I ¿(p ∧ q) es mas fuerte que p? sı
I ¿(p ∨ q) es mas fuerte que p? no
I ¿p es mas fuerte que (q → p)? sı
I ¿p es mas fuerte que q? no
I ¿p es mas fuerte que p? sı
I ¿hay una formula mas fuerte que todas? sı, por ej. false
I ¿hay una formula mas debil que todas? sı, por ej. true
39
Lenguaje de especificacion
I hasta ahora vimos logica proposicionalI es muy limitada
I nuestro objetivo es especificar (describir problemas)I vamos a usar un lenguaje mas poderoso
I permite hablar de elementos y sus propiedadesI es un lenguaje tipado
I los elementos pertenecen a distintos dominios o conjuntos(enteros, reales, etc.)
40
Tipos de datos
I conjunto de valores con operaciones
I vamos a empezar viendo tipos basicosI para hablar de un elemento de un tipo T en nuestro lenguaje,
escribimos un termino o expresionI variable de tipo TI constante de tipo TI funcion (operacion) aplicada a otros terminos (del tipo T o de
otro tipo)
I todos los tipos tienen un elemento distinguido: ⊥ o Indef
41
Tipo Bool (valor de verdad)
I valores: 1, 0 , −I constantes: true, false, ⊥ (o Indef)I conectivos logicos: ¬, ∧, ∨, →, ↔ con la semantica
trivaluada que vimos antes
I ¬A se puede escribir no(A)I (A ∧ B) se puede escribir (A && B)I (A ∨ B) se puede escribir (A || B)I (A→ B) se puede escribir (A --> B)I (A↔ B) se puede escribir (A <--> B)
I comparacion: A == B
I todos los tipos tienen esta operacion (A y B deben ser delmismo tipo T )
I es de tipo boolI es verdadero si el valor de A igual al valor de B (salvo que
alguno este indefinido - ver hoja 47)I A 6= B o A ! = B es equivalente a ¬(A == B)
I semantica secuencial42
Tipo Int (numeros enteros)I sus elementos son los de ZI constantes: 0 ; 1 ; −1 ; 2 ; −2 ; . . .I operaciones aritmeticas
I a + b (suma)I a− b (resta)I a ∗ b (multiplicacion)I a div b (division entera)I a mod b (resto de dividir a a por b)I ab o pot(a,b) (potencia)I abs(a) (valor absoluto)
I comparaciones (de tipo bool)I a < b (menor)I a ≤ b o a <= b (menor o igual)I a > b (mayor)I a ≥ b o a >= b (mayor o igual)
I β o beta. Si A es de tipo Bool, se definide como:
β(A) = beta(A) =
1 si A es verdadero
0 si A es falso
− si A es indefinido 43
Tipo Float (numeros reales)
I sus elementos son los de R
I constantes: 0 ; 1 ; −7 ; 81 ; 7,4552 ; π . . .
I operaciones aritmeticasI las mismas que Int, salvo div y modI a/b (division)I logb(a) (logaritmo)I trigonometricas
I comparaciones (de tipo bool)I las mismas que para Int
I conversion a enteroI bac o int(a)
I todos los terminos de tipo Int pueden usarse como terminosde tipo Float
44
Tipo Char (caracteres)
I sus elementos son los las letras, dıgitos y sımbolos
I constantes:‘a‘, ‘b‘, ‘c ‘, . . . , ‘z ‘, . . . , ‘A‘, ‘B‘, ‘C ‘, . . . , ‘Z ‘, . . . , ‘0‘, ‘1‘, ‘2‘, . . . , ‘9‘(en algun orden)
I funcion ord
I numera todos los caracteresI no importa mucho cual es el valor de cada uno, pero
I ord(‘a‘) + 1 == ord(‘b‘)I ord(‘A‘) + 1 == ord(‘B‘)I ord(‘1‘) + 1 == ord(‘2‘)
I funcion charI es la inversa de ord
I las comparaciones entre caracteres son comparaciones entresus ordenes
I a < b es equivalente a ord(a) < ord(b)I lo mismo para ≤, >, ≥
45
Terminos
I simplesI variables del tipo oI constantes del tipo
I complejosI combinaciones de funciones aplicadas a funciones, constantes y
variables
Ejemplos de terminos de tipo Int
I 0 + 1
I ((3 + 4) ∗ 7)2 − 1
I 2 ∗ β(1 + 1 == 2)
I 1 + ord(‘A‘)I con x variable de tipo Int; y de tipo Float; z de tipo Bool
I 2 ∗ x + 1I β(y 2 > π) + xI (x mod 3) ∗ β(z)
46
Semantica de los terminos
I vimos que los terminos representan elementos de los tiposI los terminos tienen valor indefinido cuando no se puede hacer
alguna operacionI 1 div 0I (−1)1/2
I las operaciones son estrictas (salvo los conectivos de bool)I si uno de sus argumentos es indefinido, el resultado tambien
esta indefinidoI 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)
I las comparaciones con ⊥ son indefinidasI en particular, si x esta indefinido, x == x es indefinido (no es
verdadero)
47
EspecificacionClase 3
Lenguaje de especificacion
48
Funciones auxiliares
I Facilitan la lectura y la escritura de especificaciones
I Asignan un nombre a una expresion
aux f (parametros) : tipo = e;
I f es el nombre de la funcion
I Puede usarse en el resto de la especificacion en lugar de laexpresion e
I Los parametros son opcionales
I Se reemplazan en e cada vez que se usa f
I tipo es el tipo del resultado de la funcion (el tipo de e)
Ejemplo
aux suc(x : Int) : Int = x + 1;
Puedo usarla, por ejemplo, en la poscondicion de un problema49
Definir vs. especificar
I Definimos funciones auxiliaresI Expresion del lenguaje a la que la funcion es equivalenteI Esto permite usar la funcion dentro de las especificaciones
I Especificamos problemasI Condiciones (el contrato) que deberıa cumplir alguna funcion
para ser solucion del problemaI No quiere decir que exista esa funcion o que sepamos como
escribirlaI Podrıa no haber ningun algoritmo que sirviera como solucionI Si damos la solucion va a ser en otro lenguaje (por ejemplo,
de programacion)
I En la especificacion de un problema o de un tipo no podemosusar otra funcion que hayamos especificado
50
Tipos enumerados
I Primer mecanismo para definir tipos propios
I Cantidad finita de elementos. Cada uno, representado por unaconstante
tipo Nombre = constantes;
I Nombre (del tipo): tiene que ser nuevo
I constantes: nombres nuevos separados por comas
I Convencion: todos con mayusculas
I ord(a) da la posicion del elemento en la definicion(empezando de 0)
I Opuesta: nombre u ord−1
51
Ejemplo de tipo enumerado
tipo Dıa = Lunes, Martes, Miercoles, Jueves, Viernes, Sabado, Domingo;
I ValenI ord(Lunes) == 0I Dıa(2) == MiercolesI Jueves < Viernes
I Podemos definir
aux esFinde(d : Dıa) : Bool = (d == Sabado || d == Domingo);
I Otra forma
aux esFinde2(d : Dıa) : Bool = d > Viernes;
52
Secuencias
I Tambien se llaman listasI Familia de tipos
I Para cada tipo de datos hay un tipo secuenciaI Tiene como elementos las secuencias de elementos de ese tipo
I Secuencia: varios elementos del mismo tipo, posiblementerepetidos, ubicados en un cierto orden
I Muy importantes en el lenguaje de especificacion
I [T ]: Tipo de las secuencias cuyos elementos son de tipo T
53
Notacion
I Una forma de escribir un elemento de tipo secuencia de tipoT es escribir varios terminos de tipo T separados por comas,entre corchetes
secuencia de Int: [1, 1 + 1, 3, 2 ∗ 2, 3, 5]
I La secuencia vacıa (de elementos de cualquier tipo) serepresenta []
I Se puede formar secuencias de elementos de cualquier tipoI Como las secuencias de enteros son tipos, existen por ejemplo
las secuencias de secuencias de enteros
secuencia de secuencias de enteros:
[[12, 13], [−3, 9, 0], [5], [], [], [3]]
54
Secuencias por comprensionElementos de otras secuencias que cumplan ciertas condiciones
[expresion | selectores, condiciones]
I expresion: cualquier expresion valida del lenguajeI selectores: variable ← secuencia (se puede usar ∈)
I La variable va tomando el valor de cada elemento de lasecuencia
I Las variables que aparecen en selectores se llaman ligadas, elresto de las que aparecen en una expresion se llaman libres
I condiciones: expresiones de tipo BoolI Resultado: Secuencia con el valor de la expresion calculado
para todos los elementos seleccionados por los selectores quecumplen las condiciones
Ejemplo:
[(x , y)|x ← [1, 2], y ← [1, 2, 3], x < y ] == [(1, 2), (1, 3), (2, 3)]
55
Intervalos
[expresion1..expresion2]
I Las expresiones tienen que ser del mismo tipo, discreto ytotalmente ordenado (por ejemplo, Int, Char, enumerados)
I Resultado: todos los valores del tipo entre el de la expresion1
y el de la expresion2 (ambos inclusive)
I Si no vale expresion1 ≤ expresion2, la secuencia es vacıa
I Con un parentesis en lugar de un corchete, se excluye uno delos extremos o ambos
Ejemplos:
I [5..9] == [5, 6, 7, 8, 9]
I [5..9) == [5, 6, 7, 8]
I (5..9] == [6, 7, 8, 9]
I (5..9) == [6, 7, 8]
56
Ejemplos de secuencias por comprension
Divisores comunes (asumir a y b positivos)
aux divCom(a, b : Int) : [Int] =
[x |x ← [1..a + b], divide(x , a), divide(x , b)];
aux divide(a, b : Int) : Bool = b mod a == 0;
Ejemplo:
divCom(8, 12) == [1, 2, 4]
57
Ejemplos de secuencias por comprension
Cuadrados de los elementos impares
aux cuadImp(a : [Int]) : [Int] = [x ∗ x |x ← a,¬divide(2, x)];
Ejemplo:
cuadImp([1..9)) == [1, 9, 25, 49]
58
Ejemplos de secuencias por comprension
Suma de los elementos distintos
aux sumDist(a, b : [Int]) : [Int] = [x + y |x ← a, y ← b, x 6= y ];
Ejemplo:
sumDist([1, 2, 3], [2, 3, 4, 5]) == [3, 4, 5, 6, 5, 6, 7, 5, 7, 8]
59
Operaciones con secuencias
I Longitud: long(a : [T ]) : IntI Longitud de la secuencia aI Notacion: long(a) se puede escribir |a|
I Indexacion: indice(a : [T ], i : Int) : TI requiere 0 ≤ i < |a|;I Elemento en la i-esima posicion de aI La primera posicion es la 0I Notacion: indice(a, i) se puede escribir a[i ] y tambien ai
I Cabeza: cab(a : [T ]) : TI requiere |a| > 0;I Primer elemento de la secuencia
60
Mas operaciones
I Cola: cola(a : [T ]) : [T ]I requiere |a| > 0;I La secuencia sin su primer elemento
I Pertenencia: en(t : T , a : [T ]) : BoolI Indica si el elemento aparece (al menos una vez) en la
secuenciaI Notacion: en(t, a) se puede escribir t en a y tambien t ∈ aI t /∈ a es ¬(t ∈ a)
I Agregar cabeza: cons(t : T , a : [T ]) : [T ]I Una secuencia como a, agregandole t como primer elementoI Notacion: cons(t, a) se puede escribir t : a
61
Mas operaciones
I Concatenacion: conc(a, b : [T ]) : [T ]I Secuencia con los elementos de a, seguidos de los de bI Notacion: conc(a, b) se puede escribir a + +b
I Subsecuencia: sub(a : [T ], d , h : Int) : [T ]I Sublista de a en las posiciones entre d y h (ambas inclusive)I Cuando no es 0 ≤ d ≤ h < |a|, da la secuencia vacıa
I Asignacion a una posicion:cambiar(a : [T ], i : Int, val : T ) : [T ]
I requiere 0 ≤ i < |a|;I Igual a la secuencia a, pero el valor en la posicion i es val
62
Subsecuencias con intervalos
I Notacion para obtener una subsecuencia de una secuenciadada, en un intervalo de posiciones
I Admite intervalos abiertosI a[d ..h] == sub(a, d , h)I a[d ..h) == sub(a, d , h − 1)I a(d ..h] == sub(a, d + 1, h)I a(d ..h) == sub(a, d + 1, h − 1)I a[d ..] == sub(a, d , |a| − 1)I a(d ..] == sub(a, d + 1, |a| − 1)
63
Operaciones de combinacion
I Todos verdaderos: todos(sec : [Bool]) : BoolEs verdadero solamente si todos los elementos de la secuenciason True (o la secuencia es vacıa)
I Alguno verdadero: alguno(sec : [Bool]) : BoolEs verdadero solamente si algun elemento de la secuencia esTrue (y ninguno es Indef)
64
Operaciones de combinacion
I Sumatoria: sum(sec : [T ]) : TI T debe ser un tipo numerico (Float, Int)I Calcula la suma de todos los elementos de la secuenciaI Si sec es vacıa, el resultado es 0I Notacion: sum(sec) se puede escribir
∑sec
I Ejemplo:aux potNegDosHasta(n : Int) : Float =
∑[2−m|m← [1..n]];
I Productoria: prod(sec : [T ]) : TI T debe ser un tipo numerico (Float, Int)I Calcula el producto de todos los elementos de la secuenciaI Si sec es vacıa, el resultado es 1I Notacion: prod(sec) se puede escribir
∏sec
65
Para todo
(∀ selectores, condiciones) expresion
I Termino de tipo BoolI Afirma que todos los elementos de una lista por comprension
cumplen una propiedadI Notacion: en lugar de ∀ se puede escribir paratodoI Equivale a todos([expresion | selectores, condiciones])
I Ejemplo: “todos los elementos en posiciones pares sonmayores que 5”:
auxpar(n : Int) : Bool = n mod 2 == 0;
aux posParM5(a : [Float]) : Bool =(∀i ← [0..|a|), par(i)) a[i ] > 5;
Esta expresion es equivalente a
todos([a[i ] > 5|i ← [0..|a|), par(i)]);66
Existe
(∃ selectores, condiciones)expresion
I Hay algun elemento que cumple la propiedad
I Equivale a alguno([expresion | selectores, condiciones])
I Notacion: en lugar de ∃ se puede escribir existe o existen
I Ejemplo: “Hay algun elemento de la lista que es par y mayorque 5”:
aux hayParM5(a : [Int]) : Bool = (∃x ← a, par(x)) x > 5;
Es equivalente a
alguno([x > 5|x ← a, par(x)]);
67
CantidadesI Es habitual querer contar cuantos elementos de una secuencia
cumplen una condicionI Para eso, medimos la longitud de una secuencia definida por
comprension
Ejemplos:
I “¿cuantas veces aparece el elemento x en la secuencia a?”
aux cuenta(x : T , a : [T ]) : Int = long([y |y ← a, y == x ]);
Podemos usarla para saber si dos secuencias tienen los mismoselementos (en otro orden)
aux mismos(a, b : [T ]) : Bool =(|a| == |b| ∧ (∀c ← a) cuenta(c , a) == cuenta(c, b));
I “¿cuantos primos positivos hay que sean menores a n?”
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);68
Acumulacion
acum(expresion | inicializacion, selectores, condicion)
I Notacion parecida a las secuencias por comprensionI Construye un valor a partir de una o mas secuenciasI inicializacion tiene la forma acumulador : tipoAcum = init
I acumulador es un nombre de variable (nuevo)I init es una expresion de tipo tipoAcum
I selectores y condicion: Como en las secuencias porcomprension
I expresion: Tambien, pero puede (y suele) aparecer elacumulador
I acumulador no puede aparecer en la condicion
I Significado:I El valor inicial de acumulador es el valor de initI Por cada valor de los selectores, se calcula la expresionI Ese es el nuevo valor que toma el acumuladorI El resultado de acum es el resultado final del acumulador (de
tipo tipoAcum) 69
Ejemplos de acumulacionI aux sum(l : [Float]) : Float = acum(s+i | s : Float = 0, i ← l);
I Productoria
aux prod(l : [Float]) : Float =acum(p ∗ i | p : Float = 1, i ← l);
I Fibonacciaux fiboSuc(n : Int) : [Int] =
acum(f ++[f [i−1]+f [i−2]] | f : [Int] = [1, 1], i ← [2..n]);
I si n ≥ 1, devuelve los primeros n + 1 numeros de FibonacciI si n == 0, devuelve [1, 1]
I por ejemplo, fiboSuc(5) == [1, 1, 2, 3, 5, 8]
I n-esimo numero de Fibonacci (n ≥ 1)
aux fibo(n : Int) : Int = (fiboSuc(n − 1))[n − 1];
I por ejemplo, fibo(6) == 8
70
Mas ejemplos de acumulacion
I Concatenacion de elementos de una secuencia de secuencias(aplanar)
aux concat(a : [[T ]]) : [T ] =acum(l + +c | l : [T ] = [], c ← a);
I por ejemplo, concat([[1, 2], [3], [4, 5, 6]]) == [1, 2, 3, 4, 5, 6]
I Secuencias por comprension
El termino [expresion | selectores, codicion] es equivalente a
acum(res + +[expresion] | res : [T ] = [], selectores, codicion);
71
Especificacion de problemas
I Problema: funcion a definir
I Especificacion: contrato que debe cumplir la funcion para serconsiderada solucion del problema
I Hay que distinguirI el que (especificacion, el contrato a cumplir)I el como (implementacion de la funcion)
I Sin referencias a posibles metodos para resolver el problemaI Distintas soluciones a un mismo problema
I Distintos lenguajes de programacionI Para el mismo lenguaje de programacion, distintas formas de
programar una funcion que cumpla con la especificacionI Cada algoritmo posible es una solucion distinta para el
problema
I Conclusion: hay un conjunto (tal vez vacıo) de algoritmos quecumplen cada especificacion
72
Ejemplos de especificacionI Calcular el cociente de dos enteros
problema division(a, b : Int) = result : Int {requiere b 6= 0;asegura result == a div b;
}
I Calcular el cociente y el resto, para divisor positivoI Necesitamos devolver dos valoresI Usamos una tuplaI El tipo (Int, Int) son los pares ordenados de enterosI prm y sgd devuelven sus componentes
problema cocienteResto(a, b : Int) = result : (Int,Int) {requiere b > 0;asegura a == q ∗ b + r ∧ 0 ≤ r < b;aux q = prm(result), r = sgd(result);
}73
Parametros modificablesI Alternativa 2
I Unico resultado: el cocienteI Resto: parametro modificable
problema cocienteResto2(a, b, r : Int) = q : Int {requiere b > 0;modifica r ;asegura a == q ∗ b + r ∧ 0 ≤ r < b;
}
I Alternativa 3I Otro parametro para el cocienteI La funcion no tiene resultado
problema cocienteResto3(a, b, q, r : Int){requiere b > 0;modifica q, r ;asegura a == q ∗ b + r ∧ 0 ≤ r < b;
}74
Mas ejemplos de especificacion
I Sumar los inversos multiplicativos de varios reales
Como no sabemos la cantidad, usamos secuencias
problema sumarInvertidos(a : [Float]) = result : Float {requiere 0 /∈ a;asegura result =
∑[1/x | x ← a];
}
I Precondicion: que el argumento no contenga ningun 0I Si no, la poscondicion podrıa indefinirseI Formas equivalentes:
I requiere (∀x ← a) x 6= 0;I requiere ¬(∃x ← a) x == 0;I requiere ¬(∃i ← [0..|a|)) a[i ] == 0;
75
Mas ejemplos de especificacionI Encontrar una raız de un polinomio de grado 2 a coeficientes
reales
problema unaRaızPoli2(a, b, c : Float) = r : Float {asegura a ∗ r ∗ r + b ∗ r + c == 0;
}I No tiene precondicion (la precondicion es True)I Pero si a == 1, b == 0 y c == 1, no existe ningun r tal que
r ∗ r + 1 == 0I Especificacion que no puede ser cumplida por ninguna funcionI La precondicion es demasiado debilI La nueva precondicion podrıa ser:
requiere b ∗ b ≥ 4 ∗ a ∗ c ;I La poscondicion describe que hacer y no como hacerlo
No dice como calcular la raız, ni que raız devolverI Sobrespecificacion: poscondicion que pone mas restricciones de
las necesarias (fija la forma de calcular la solucion)Ejemplo: asegura r == (−b + (b2 − 4 ∗ a ∗ c)1/2)/(2 ∗ a)
I esto serıa sobreespecificar aun con a 6= 0 en la precondicion76
Otro ejemplo
Encontrar el ındice (la posicion) del menor elemento en unasecuencia de numeros reales distintos no negativos
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 ;
}I Nombramos las precondiciones, para aclarar su significado
I Los nombres tambien pueden usarse como predicados encualquier lugar de la especificacion
I El nombre no alcanza, hay que escribir la precondicion en ellenguaje
I Otra forma de escribir la segunda precondicion:requiere Distintos2: (∀i ← [0..|a|)) a[i ] /∈ a[0..i);
77
EspecificacionClase 4
Tipos compuestos
78
Tipos compuestos
I cada valor de un tipo basico representa un elemento atomico,indivisible
I IntI FloatI BoolI Char
I un valor de un tipo compuesto contiene informacion quepuede ser dividida en componentes de otros tipos
I ya vimos dos ejemplos de tipos compuestos:I secuencias: un valor de tipo secuencia de enteros tiene varios
componentes, cada uno es un enteroI tuplas: un valor de tipo par ordenado de enteros tiene dos
componentes
I para definir un tipo compuesto, tenemos que darleI un nombreI uno o mas observadores
79
Observadores
I funciones que se aplican a valores del tipo compuesto ydevuelven el valor de sus componentes
I pueden tener mas parametros
I definen el tipo compuestoI si dos terminos del tipo dan el mismo resultado para todos los
observadores, se los considera igualesI esta es la definicion implıcita de la igualdad == para tipos
compuestos (el == esta en todos los tipos)
I pueden tener precondicionesI si vale la precondicion, no pueden devolver un valor indefinido
I no tienen poscondiciones propias
I si hay condiciones generales que deben cumplir los elementosdel tipo, se las presenta como invariantes de tipo
80
Tipo PuntoI un punto en el planoI componentes: coordenadas (x e y)I un observador para obtener cada una
tipo Punto {observador X (p : Punto) : Float;observador Y (p : Punto) : Float
}I especificacion una funcion que recibe dos numeros reales y
construye un punto con esas coordenadas:
problema nuevoPunto(a, b : Float) = res : Punto {asegura X (res) == a;asegura Y (res) == b;
}
Cuando el resultado es de un tipo compuesto, usamos losobservadores para describir el resultado
I funcion auxiliar para calcular la distancia entre dos puntosaux dist(p, q : Punto) : Float =
((X (p)− X (q))2 + (Y (p)− Y (q))2
)1/2
81
Tipo CırculoI sus componentes son de tipos distintos
tipo Cırculo {observador Centro(c : Cırculo) : Punto;observador Radio(c : Cırculo) : Float;invariante Radio(c) > 0;
}I especificar el problema de construir un cırculo a partir de
I su centro y su radio
problema nuevoCırculo(c : Punto, r : Float) = res : Cırculo {requiere r > 0;asegura (Centro(res) == c ∧ Radio(res) == r);
}I su centro y un punto sobre la circunferencia:
problema nuevoCırculoPuntos(c , x : Punto) = res : Cırculo {requiere dist(c , x) > 0;asegura (Centro(res) == c ∧ Radio(res) == dist(c , x));
}82
Invariantes de tipo
I son la garantıa de que los elementos esten bien construidos
I deben cumplirse para cualquier elemento del tipo
I los problemas que reciban valores del tipo compuesto comoargumentos pueden suponer que se cumplen los invariantes
tipo T {observador . . . ;
...invariante R(x);
}
problema probA(. . . , x : T, . . . ) = . . . {requiere . . . ∧ R(x)︸︷︷︸
no hace falta
∧ . . . ;
asegura . . . ;
}
83
Tipos genericosI en los ejemplos vistos, cada componente es de un tipo
determinado de antemanoI tambien hay tipos compuestos que representan una estructura
I su contenido van a ser valores no siempre del mismo tipoI comportamiento uniforme
I se llaman tipos genericos o tipos parametricosI definen una familia de tipos
I cada elemento de la familia es una instancia del tipo genericoI los problemas que usen un tipo generico definen una familia de
problemasI permiten definir funciones auxiliares o especificar problemas
I genericos: su descripcion depende unicamente de la estructuradel tipo generico
I especıficos de un tipo: su descripcion solo tiene sentido paraun cierto tipo
I el nombre del tipo tiene parametrosI variables que representan a los tipos de los componentesI los parametros de tipo se escriben entre los sımbolos 〈 y 〉
despues del nombre del tipo
84
Tipo Matriz〈T 〉I tipo generico de las matrices cuyos elementos pertenecen a un
tipo cualquiera T
tipo Matriz〈T 〉{observador filas(m : Matriz〈T 〉) : Int;observador columnas(m : Matriz〈T 〉) : Int;observador val(m : Matriz〈T 〉, f , c : Int) : T {
requiere 0 ≤ f < filas(m);requiere 0 ≤ c < columnas(m);
}
precondicion del observador
invariante filas(m) > 0;invariante columnas(m) > 0;
}I ejemplo: una instancia del tipo generico Matriz〈T 〉 es
Matriz〈Char〉I el tipo de matrices cuyos elementos son caracteres
I un tipo generico define una familia (infinita) de tiposI algunos miembros de la familia Matriz〈T 〉 son
Matriz〈Int〉 , Matriz〈Punto〉 , Matriz〈Matriz〈Float〉〉85
Operaciones genericas con matrices
I expresion que cuenta la cantidad de elementos de una matriz
aux elementos(m : Matriz〈T 〉) : Int = filas(m) ∗ columnas(m);
I especificacion del problema de cambiar el valor de unaposicion de una matriz
problema cambiar(m : Matriz〈T 〉, 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);
}
86
Operaciones sobre matrices para tipos instanciados
I expresion que suma los elementos de una matriz de enteros
aux suma(m : Matriz〈Int〉) : Int =∑[val(m, i , j) | i ← [0..filas(m)), j ← [0..columnas(m))];
I especificacion del problema de construir la matriz identidad den × n:
problema matId(n : Int) = ident : Matriz〈Int〉 {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 ¿importa el orden?I sı; si estuviera al reves, se podrıa indefinir val(ident, i , i)I en este caso, se indefinirıa la poscondicion, valiendo la
precondicion
87
El tipo Secuencia〈T 〉
I ya lo usamos con su nombre alternativo: [T ]
I presentamos tambien sus observadores: longitud e indexacion
tipo Secuencia〈T 〉{observador long(s : Secuencia〈T 〉) : Int;observador ındice(s : Secuencia〈T 〉, i : Int) : T{
requiere 0 ≤ i < long(s);−→ precondicion de observador}
}I notaciones alternativas
I |s| para la longitudI si o s[i ] para la indexacion
88
Operaciones genericas con secuencias
I podemos definirI 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], (0 ≥ ∧h < |a|)];I aux todos(sec : [Bool]) : Bool = false /∈ sec ;I aux alguno(sec : [Bool]) : Bool = true ∈ sec ;
I para algunas no usamos directamente los observadores
I aprovechamos la notacion de listas por comprension
I el selector es parte de la notacion de listas por comprension(estructura especial de nuestro lenguaje de especificacion)
89
Operaciones sobre secuencias para tipos instanciadosI para representar textos (cadenas de caracteres) usamos el tipo
Secuencia〈Char〉 (o [Char])I queremos ver si alguna palabra de una lista aparece en un libro
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);
}I lista de palabras: tipo [[Char]] o Secuencia〈Secuencia〈Char〉〉
I secuencias que en cada posicion tienen una secuencia decaracteres
I precondicionesI cada palabra debe tener al menos una letraI no contienen ningun espacio (dejarıan de ser palabras)
I en vez de SinEspacios: que las palabras no contengan ningunsımbolo que no sea una letra
requiere SinSımbolos: (∀p ← palabras) soloLetras(p);aux letras: [Char ] = [‘A‘..‘Z ‘] + +[‘a‘..‘z ‘];
aux soloLetras(s : [Char]) : Bool = (∀l ← s) l ∈ letras; 90
Tuplas
I ya las mencionamos
I secuencias de tamano fijo
I cada elemento puede pertenecer a un tipo distinto
I ejemplos: pares, ternas
tipo Par〈A,B〉{observador prm(p : Par〈A,B〉) : A;observador sgd(p : Par〈A,B〉) : B;
}
tipo Terna〈A,B,C 〉{observador prm3(t : Terna〈A,B,C 〉) : A;observador sgd3(t : Terna〈A,B,C 〉) : B;observador trc3(t : Terna〈A,B,C 〉) : C ;
}I notacion
I Par〈A,B〉 tambien se puede escribir (A,B)I Terna〈A,B,C 〉 tambien se puede escribir (A,B,C )
91
ifThenElse〈T 〉Funcion que elige entre dos elementos del mismo tipo, segun unacondicion (guarda)
I si la guarda es verdadera, elige el primeroI si no, elige el segundo
Por ejemplo
I expresion que devuelve el maximo entre dos elementos:
aux max(a, b : Int) : Int = ifThenElse〈Int〉(a > b, a, b)
cuando los argumentos se deducen del contexto, se puedeescribir directamente
aux max(a, b : Int) : Int = ifThenElse(a > b, a, b) o bienaux max(a, b : Int) : Int = if a > b then a else b
I expresion 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︸ ︷︷ ︸no se indefine cuando x = 0
92
Mas 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 cambiar el valor de una posicion
aux cambiar(a : [T ], i : Int, v : T ) : [T ] =[if i 6= j then a[i ] else v | j ← [0..|a|)];
93
Programacion funcionalClase 1
Funciones Simples - recursion - tipos de datos
94
Algoritmos y programas
I aprendieron a especificar problemasI el objetivo es ahora escribir un algoritmo que cumpla esa
especificacionI secuencia de pasos que pueden llevarse a cabo mecanicamente
I puede haber varios algoritmos que cumplan una mismaespecificacion
I una vez que se tiene el algoritmo, se escribe el programaI expresion formal de un algoritmoI lenguajes de programacion
I sintaxis definidaI semantica definidaI que hace una computadora cuando recibe ese programaI que especificaciones cumpleI ejemplos: Haskell, C, C++, C#, Java, Smalltalk, Prolog, etc.
95
Paradigmas
I paradigmas de programacionI formas de pensar un algoritmo que cumpla una especificacionI cada uno tiene asociado un conjunto de lenguajesI nos llevan a encarar la programacion segun ese paradigma
I Haskell: paradigma de programacion funcionalI programa = coleccion de funciones
I aparatos que transforman datos de entrada en un resultado
I los lenguajes funcionales nos dan herramientas para explicarlea la computadora como calcular esas funciones
96
Expresiones
I tira de sımbolos que representan (denotan) un valorI ejemplos:
I 2I 1+1I (3*7+1)/11I todas representan el mismo valor
I los valores se agrupan en tiposI como en el lenguaje de especificacion: Int, Float, Bool, CharI hay tambien tipos compuestos (por ejemplo, pares ordenados)
97
Transparencia referencial
I propiedad muy importante de la programacion funcional
Una expresion representa siempre el mismo valoren cualquier lugar de un programa
I otros paradigmas: el significado de una expresion depende delcontexto
I muy util al modificar programasI modificar una parte no afecta otras
I tambien para verificar (demostrar que se cumple laespecificacion)
I podemos usar propiedades ya probadas para sub expresionesI valor no depende de la historiaI valen en cualquier contexto
98
Formacion de expresiones
I expresiones atomicas (las mas simples)I tambien se llaman formas normalesI son la forma mas intuitiva de representar un valorI ejemplos
I 2I FalseI (3, True)
I es comun llamarlas “valores”I aunque no son un valor, representan un valor, como las demas
expresiones
I expresiones compuestasI se construyen combinando expresiones atomicas con
operacionesI ejemplos:
I 1+1I 1==2I (4-1, True || False)
99
Expresiones mal formadas
I algunas cadenas de sımbolos no forman expresionesI por problemas sintacticos
I +*1-I (TrueI (’a’,)
I o por error de tiposI 2 + FalseI 2 || ’a’I 4 * ’b’
I para saber si una expresion esta bien formada, aplicamosI reglas sintacticasI reglas de asignacion de tipos (o de inferencia de tipos)
100
Aplicacion de funciones
En programacion funcional (como en matematica) las funcionesson elementos (valores)
I una funcion es un valorI la operacion basica que podemos realizar con ese valor es la
aplicacionI aplicar la funcion a un elemento para obtener un resultado
I sintacticamente, la aplicacion se escribe como unayuxtaposicion (la funcion seguida de su parametro)
I f es una funcion y e un elemento de su conjunto de partidaI f e denota el elemento que se relaciona con e por medio de la
funcion f
I por ejemplo, doble 2 representa al numero 4
101
Ecuaciones
I Dada una expresion, ¿como sabemos que valor denota?I Usando las ecuaciones que la definen
I por ejemplo: doble x = x + x
I se reemplaza cada sub expresion por otras segun lasecuaciones
I pero este proceso puede no terminarI aun con ecuaciones bien pensadas
doble (1 + 1)I reemplazo 1 + 1 por doble 1
doble (doble 1)I reemplazo doble 1 por 1 + 1I volvı a empezar...
102
Ecuaciones orientadasI solucion: usar ecuaciones orientadas
I lado izquierdo: expresion a definirI lado derecho: definicion
I calculo de valores = reemplazar subexpresiones que sean ladoizquierdo de una ecuacion por su lado derecho
Ejemplo: doble x = x + xdoble (1 + 1) (1 + 1) + (1 + 1) 2 + (1 + 1) 2 + 2 4
Tambien podrıa ser:doble (1 + 1) doble 2 2 + 2 4
Mas adelante veremos como funciona Haskell en particular.
I reduccion = reemplazar una subexpresion por su definicion,sin tocar el resto
I la expresion resultante puede no ser mas cortaI pero seguramente esta “mas definida”
I mas cerca de ser una forma normal
103
Programa funcional
I conjunto de ecuaciones que definen una o mas funcionesI ¿para que se usa un programa funcional?
I para reducir expresionesI puede no ser tan claro que esto resuelva un problemaI las ecuaciones orientadas, junto con el mecanismo de reduccion
describen algoritmos: pasos para resolver un problema
Ejemplos:
I doble x = x+x
I fst (x,y) = x
I dist (x,y) = sqrt (x^2+y^2)
I signo 0 = 0
signo x | x > 0 = 1
signo x | x < 0 = -1
I promedio1 (x,y) = (x+y)/2
I promedio2 x y = (x+y)/2
I fact 0 = 1
fact n | n > 0 = n * fact (n-1)
I fib 1 = 1
fib 2 = 1
fib n | n > 2 = fib (n-1) + fib (n-2)
104
Definiciones recursivas
I en el cuerpo de la definicion de fact y fib (lado derecho)aparece el nombre de la funcion
I propiedades de la definicionI tiene que tener uno o mas casos bases
I en el caso de fact el caso base es
fact 0 = 1
I en el caso de fib los casos bases son
fib 1 = 1 y fib 2 = 1
I las llamadas recursivas del lado derecho tienen que acercarsemas al caso base
I en el caso de fact la llamada recursiva es
fact (n-1)
I en el caso de fib las llamadas recursivas son
fib (n-1) y fib (n-2)
105
Asegurarse de llegar a un caso base
Supongamos esta especificacion:problema par(n : Int) = result : Bool{
requiere n ≥ 0;asegura result == (n mod 2 == 0);
}I ¿este programa cumple con la especificacion?
par 0 = Truepar n = par (n-2)
I no, porque se indefine para los impares positivos
I se arregla de alguna de estas formas:
par 0 = Truepar 1 = Falsepar n = par (n-2)
par 0 = Truepar n = not (par (n-1))
106
Correctitud
I para demostrar que una definicion es correcta, hace falta una especificaciony un programa:
problema fact(n : Int) = result : Int{requiere n ≥ 0;asegura result ==
∏[1..n];
}
fact 0 = 1fact n | n > 0 = n * fact (n-1)
I la especificacion (o cualquier otro comentario) se puede escribir en elmismo programa funcional precediendo cada linea con -- (doble menos)
I no se considera parte del programaI por ejemplo:
-- problema fact (n: Int) = result: Int {-- requiere n >= 0;-- asegura result == prod [1..n];-- }fact 0 = 1fact n | n > 0 = n * fact (n-1)
107
Demostracion de correctitudSi la definicion es recursiva, lo mas natural es demostrar porinduccion
I caso base (n == 0)I la especificacion dice que hay que probar que la funcion fact
con entrada 0 devuelve∏
[1..0] ==∏
[ ] = 1I pero eso es justamente lo que devuelve fact en el caso base
I hipotesis inductiva (HI): fact(n) ==∏
[1..n]I la especificacion dice que hay que probar que la funcion fact
con entrada n + 1 devuelve∏
[1..n + 1]I calculemos fact(n + 1)I gracias a la precondicion, n + 1 > 0. Entonces uso la segunda
ecuacion instanciando en n + 1
fact(n + 1) == (n + 1) ∗ fact(n)
I por HI:
fact(n + 1) == (n + 1) ∗∏
[1..n]
==∏
[1..(n + 1)]
I entonces, demostre que fact con entrada n + 1 devuelve∏[1..(n + 1)]
108
Tipos de datosI es un concepto que vimos en el lenguaje de especificacionI conjunto de valores a los que les pueden aplicar las mismas
operacionesI en Haskell, todo valor pertenece a algun tipo
I las funciones son valores, y tambien tienen tipoI ejemplo: el tipo “funciones de enteros en enteros”
I todo valor pertenece a un tipo. Toda expresion (bien formada)denota un valor. Entonces, toda expresion tiene un tipo (el delvalor que representa)
Haskell es un lenguaje fuertemente tipadoI no se pueden pasar elementos de un tipo a una operacion que
espera argumentos de otro
Tambien tiene tipado estaticoI no hace falta hacer funcionar un programa para saber de
que tipo son sus expresionesI el interprete puede controlar si un programa tiene errores de
tipos109
Notacion para tiposI e :: T
I la expresion e es de tipo TI el valor denotado por e pertenece al conjunto de valores
llamado TI ejemplos:
I 2 :: IntI False :: BoolI ’b’:: CharI doble :: Int -> Int
I sirve para escribir reglas y razonar sobre HaskellI tambien se usa dentro de Haskell
I indica de que tipo queremos que sean los nombres quedefinimos
I el interprete chequea que el tipo coincida con el de lasexpresiones que lo definen
I podemos obviar las declaraciones de tipos pero nos perdemosla oportunidad del doble chequeo
I existen casos en los que sı es obligatorio, para dirimirambiguedades
110
PolimorfismoI el tipado de Haskell es fuerteI pero hay funciones que pueden aplicarse a distintos tipos de
datos (sin redefinirlas)I se llama polimorfismo
I se usa cuando el comportamiento de la funcion no depende delvalor de sus parametros
I lo vimos en el lenguaje de especificacion con las funcionesgenericas
I ejemplo: la funcion identidad: id x = xI ¿de que tipo es id?
I id 3: Por las reglas, id :: Int -> IntI id True: Por las reglas, id :: Bool -> BoolI id doble: Por las reglas, id :: (Int -> Int) -> (Int ->
Int)I la idea es id :: a -> a
I sin importar que tipo sea aI en Haskell se escribe usando variables de tipo.I “id es una funcion que dado un elemento de algun tipo a
devuelve otro elemento de ese mismo tipo”I indica que id es de de un tipo parametrico (depende de un
parametro)I id es una funcion polimorfica
111
Declaraciones de tipo
I doble :: Int -> Int
doble x = x+x
I fst :: (a,b) -> a
fst (x,y) = x
I dist :: (Float, Float) -> Float
dist (x,y) = sqrt (x^2+y^2)
I signo Int -> Int
signo 0 = 0
signo x | x > 0 = 1
signo x | x < 0 = -1
I promedio1 :: (Float, Float) -> Float
promedio1 (x,y) = (x+y)/2
I promedio2 :: Float -> Float -> Float
promedio2 x y = (x+y)/2
I fact :: Int -> Int
fact 0 = 1
fact n | n > 0 = n * fact (n-1)
I fact :: Int -> Int
fib 1 = 1
fib 2 = 1
fib n | n > 2 = fib (n-1) + fib (n-2)
112
Currificacion
I diferencia entre promedio1 y promedio2I promedio1 :: (Float, Float) -> Float
promedio1 (x,y) = (x+y)/2I promedio2 :: Float -> Float -> Float
promedio2 x y = (x+y)/2
I solo cambia el tipo de datos de la funcionI promedio1 recibe un solo parametro (un par)I promedio2 recibe dos Float separados por un espacioI para declararla, separamos los tipos de los parametros con una
flechaI tiene motivos teoricos y practicos (que no veremos ahora)
I la notacion se llama currificacion en honor al matematicoHaskell B. Curry
I para nosotros, alcanza con ver que evita el uso de variossignos de puntuacion (comas y parentesis)
I promedio1 (promedio1 (2, 3), promedio1 (1, 2))I promedio2 (promedio2 2 3) (promedio2 1 2)
113
Programacion funcionalClase 2
Tipos algebraicos
114
Tipos algebraicos y abstractos
I ya vimos los tipos basicosI IntI FloatI CharI Bool
I otros tipos de datos:I tipos algebraicoI tipos abstracto
I tipo algebraicoI conocemos la forma que tiene cada elementoI tenemos un mecanismo para inspeccionar como
esta construido cada dato
I tipo abstractoI solo conocemos sus operacionesI no sabemos como estan formados los elementosI la unica manera de obtener informacion sobre ellos es mediante
las operaciones
115
Tipos algebraicos
I para crear un tipo algebraico decimos que forma va a tenercada elemento
I se hace definiendo constantes que se llaman constructoresI empiezan con mayuscula (como los tipos)I pueden tener argumentos, pero no hay que confundirlos con
funcionesI no tienen reglas de inferencia asociadaI forman expresiones atomicas (valores)
I ejemplo muy sencillo de tipo algebraico: BoolI tiene dos constructores (sin argumentos)
True :: BoolFalse :: Bool
116
Definicion de tipos algebraicosI clausulas de definicion de tipos algebraicos
I empiezan con la palabra dataI definen el tipo y sus constructoresI cada constructor da una alternativa distinta para construir un
elemento del tipoI los constructores se separan entre sı por barras verticales
I data Sensacion = Frıo | CalorI tiene dos constructores sin parametrosI el tipo tiene unicamente dos elementos, como el tipo Bool
I data Figura = Cırc Float | Rect Float FloatI dos constructores con parametrosI algunas figuras son cırculos y otras rectangulos
I los cırculos se diferencian por un numero (su radio)I los rectangulos, por dos (su base y su altura)
I ejemplos:I c1 = Cırc 1I c2 = Cırc (4.5 - 3.5)I cırculo x = Cırc (x+1)I r1 = Rect 2.5 3I cuadrado x = Rect x x
117
Pattern matching
I correspondencia o coincidencia de patrones
I mecanismo para ver como esta construido un elemento de untipo algebraico
I si definimos una funcion que recibe como parametro unaFigura, podemos averiguar si es un cırculo o un rectangulo (ycon que parametros fue construida)
I patterns: expresiones del lenguaje formadas solamente porconstructores y variables que no se repiten
I Rect x y es un patronI 3 + x no es un patronI Rect x x tampoco porque tiene una variable repetida
I matching: operacion asociada a un patronI dada una expresion cualquiera dice si su valor coincide por su
forma con el patronI si la correspondencia existe, entonces liga las variables del
patron a las subexpresiones correspondientes
118
Ejemploarea :: Figura -> Float
area (Cırc radio) = pi * radio * radioarea (Rect base altura) = base * altura
cırculo :: Float -> Figuracırculo x = Cırc (x+1)
I lado izquierdo: funcion que estamos definiendo aplicada a unpatron
I evaluemos la expresion area (cırculo 2)I el interprete debe elegir cual de las ecuaciones de area utilizarI primero debe evaluar cırculo 2 para saber a que constructor
correspondeI la reduccion da Cırc (2+1)I ya se puede verificar cada ecuacion de area para buscar el
matchingI se logra con la primera ecuacion
I radio queda ligada a (2+1)
I luego de varias reducciones (aritmeticas) mas, se llega al valorde la expresion: 28.2743
119
Sinonimos de tiposI se usa la clausula type para darle un nombre nuevo a un tipo
existenteI no se crea un nuevo tipo, sino un sinonimo de tipoI los dos nombres son equivalentes
I ejemplo: nombrar una instancia particular de un tipoparametrico: type String = [Char]
I ejemplo: renombrar un tipo existente con un nombre massignificativo:type Nombre = Stringtype Sueldo = Inttype Empleado = (Nombre, Sueldo)type Direccion = String
type Persona = (Nombre, Direccion)
I Persona es un par de String, pero (String, String), esmas difıcil de entender
I tambien hay sinonimos de tipos parametricos:type IntY a = (Int, a)
120
Tipos recursivos
El tipo definido es argumento de alguno de los constructores
Ejemplo:
I data N = Z | S NI tiene dos constructores:
1. Z es un constructor sin argumentos2. S es un constructor con argumentos (de tipo N)
I elementos del tipo N:
Z , S Z , S (S Z) , S (S (S Z)) , . . .↓ ↓ ↓ ↓0 1 2 3
Este tipo puede representar a los numeros naturales.
121
Ejemplos
Usando pattern matching, podemos definir funciones recursivassobre cualquier termino mediante recursion estructural.
I suma :: N -> N -> N
suma n Z = n
suma n (S m) = S (suma n m)
I producto :: N -> N -> N
producto n Z = Z
producto n (S m) = suma n (producto n m)
I menorOIgual :: N -> N -> Bool
menorOIgual Z m = TruemenorOIgual (S n) Z = False
menorOIgual (S n) (S m) = menorOIgual n m
122
Otro ejemplo
I data P = T | F | A P P | N PI tiene cuatro constructores:
1. T y F son constructores sin argumentos2. A es un constructor con dos argumentos (de tipo P)3. N es un constructor con un argumento (de tipo P)
I elementos del tipo P:
T , F , A T F , N (A T F) , . . .↓ ↓ ↓ ↓
true false (true ∧ false) ¬(true ∧ false)
Este tipo puede representar a las formulas proposicionales.
123
Ejemplos
I contarAes :: P -> Int
contarAes T = 0contarAes F = 0contarAes (N x) = contarAes x
contarAes (A x y) = 1 + (contarAes x) + (contarAes y)
I valor :: P -> Bool
valor T = Truevalor F = Falsevalor (N x) = not (valor x)
valor (A x y) = (valor x) && (valor y)
124
ListasI se usan mucho (el igual que en el lenguaje de especificacion)I son un tipo algebraico recursivo parametrico
I en especificacion las vimos definidas con observadoresI en Haskell, se definen con constructores
I primero, vamos a definirlas como un tipo algebraico comun
data List a = Nil | Cons a (List a)
I interpretamosI Nil como la lista vacıaI Cons x l como la lista que resulta de agregar x como
primer elemento de lI por ejemplo,
I List Int es el tipo de las listas de enteros. Son de este tipo:I NilI Cons 2 NilI Cons 3 (Cons 2 Nil)
I List (List Int) es el tipo de las listas de listas de enteros.Son de este tipo:
I NilI Cons Nil NilI Cons (Cons 2 Nil) (Cons Nil Nil) 125
Ejemplos
I calcular la longitud de una lista
longitud :: List a -> Intlongitud Nil = 0
longitud (Cons x xs) = 1 + (longitud xs)
I sumar los elementos de una lista de enteros
sumar :: List Int -> Intsumar Nil = 0
sumar (Cons x xs) = x + (sumar xs)
I concatenar dos listas
concat :: List a -> List a -> List aconcat Nil ys = ys
concat (Cons x xs) ys = Cons x (concat xs ys)
126
Notacion de listas en HaskellI List a se escribe [a]I Nil se escribe []I (Cons x xs) se escribe (x:xs)I los constructores son : y []
I Cons 2 (Cons 3 (Cons 2 (Cons 0 Nil)))I (2 : (3 : (2 : (0 : []))))I 2 : 3 : 2 : 0 : []
I notacion mas comoda: [2,3,2,0]
Ejemplos (todas estan en el preludio)I length :: [a] -> Int
length [] = 0
length (x:xs) = 1 + (length xs)I sum :: [Int] -> Int
sum [] = 0
sum (x:xs) = x + (sum xs)
I (++) :: [a] -> [a] -> [a][] ++ ys = ys
(x:xs) ++ ys = x:(xs ++ ys)
++ concatena dos listas.Es un operador que se usacon notacion infija[1, 2] + +[3, 4, 5] 127
Arboles
I estructuras formadas por nodos
I almacenan valores de manera jerarquica (cada nodo guarda unvalor)
I vamos a trabajar con arboles binariosI de cada nodo salen cero o dos ramasI hoja: es un nodo que no tiene ramas
Ejemplos:
2 2
4 5
2
4 5
0 1
2
4
7 9
5
0 1
128
Definicion del tipo ArbolDefinicion del tipo
data Arbol a = Hoja a | Nodo a (Arbol a) (Arbol a)
2
2
4 5
2
4 5
0 1
−→ Hoja 2
−→ Nodo 2 (Hoja 4) (Hoja 5)
−→ Nodo 2 (Hoja 4) (Nodo 5 (Hoja 0) (Hoja 1))
hojas :: Arbol a -> Inthojas (Hoja x) = 1hojas (Nodo x i d) = (hojas i) + (hojas d)
altura :: Arbol a -> Intaltura (Hoja x) = 1
altura (Nodo x i d) = 1 + ((altura i) ’max’ (altura d))129
Recorridos en arbolesI las funciones que vimos van recorriendo un arbol y operando
con cada nodoI podrıan hacerlo en cualquier orden con el mismo resultadoI a veces es importante el orden al recorrer un arbol para
aplicarle alguna operacion
inOrder, preOrder :: Arbol a -> [a]
inOrder (Hoja x) = [x]inOrder (Nodo x i d) = (inOrder i) ++ [x] ++ (inOrder d)
preOrder (Hoja x) = [x]
preOrder (Nodo x i d) = x : ((preOrder i) ++ (preOrder d))
Por ejemplo, para el arbol A
2
4 5
0 1
inOrder A es [4,2,0,5,1]
preOrder A es [2,4,5,0,1]
130
Programacion funcionalClase 3
Tipos abstractos
131
Tipos abstractos
I Vimos como crear y como usar tipos de datos algebraicosI Tambien se puede recibir un tipo como abstracto
I No se puede acceder a su representacion
I No hace falta mas de un programadorI Creo un tipo (algebraico) y oculto su representacionI En otras partes del programa me esta prohibido accederla
I En el diseno de lenguajes de programacion es un punto claveI Facilitar que el programador haga lo que quiere, pero alentarlo
a hacerlo bienI Y, mas importante, impedirle hacerlo mal: usar la
representacion interna del tipo en otros programas
I Dos motivosI Criterios basicos para tipos algebraicosI Mantenimiento
132
Criterios para tipos algebraicos
1. Toda expresion del tipo representa un valor validoI ConstructorI Valores cualesquiera para sus parametros
2. Igualdad por construccionI Dos valores son iguales solamente si se construyen igual
I Mismo constructorI Mismos argumentos
Condiciones idealesI A veces se construyen tipos algebraicos sin respetarlas
I Cırc (-1.2)I Rect 2.3 4.5 6= Rect 4.5 2.3
133
Ejemplo de tipo algebraico: Complejo
I Toda combinacion de dos Float es un complejo
I Dos complejos son iguales sii sus partes reales y sus partesimaginarias coinciden
data Complejo = C Float Float
parteReal, ParteImag :: Complejo -> Float
ParteReal (C r i) = rParteImag (C r i) = i
hacerPolar :: Float -> Float -> ComplejohacerPolar rho theta =
C (rho * cos theta) (rho * sin theta)
134
Racionales
data Racional = R Int Int
numerador, denominador :: Racional -> Int
numerador (R n d) = n
denominador (R n d) = d
¿Esta bien definido? ¡No!
I No todo par de enteros es un racional: R 1 0
I Hay racionales iguales con distinto numerador y denominador:R 4 2 y R 2 1
135
Racionales como tipo algebraico
Se puede usar, pero con mucho cuidadoI Al construir
I Nunca segunda componente 0
I Al definir funcionesI Mismo resultado para toda representacion del mismo racionalI Funcion numerador (devuelve la primera componente)
I Resultados distintos para R (-1) (-2) y R 2 4I Son el mismo numero, no estamos representando las fracciones
Tipos abstractos
I Ayuda del lenguaje
I La representacion interna se usa en pocas funciones
I Mensaje de error si se intenta violar ese acuerdo
136
Mantenimiento
I Acceso por pattern matching limitado a pocas funcionesI Si cambia la representacion interna, solamente hay que
cambiar esas funcionesI En lo posible, muy cerca una de otra en el codigo (mismo
archivo)
I Si no pedimos al lenguaje esta proteccionI Riesgo de usar la implementacion en otros programasI Cuando la cambiemos, dejan de funcionarI Hay que modificar uno por uno
137
Uso de tipos abstractos
I Se recibe un tipo de datos abstractoI nombre del tipoI nombres de sus operaciones basicasI tipos de las operacionesI especificacion de su funcionamiento
I puede ser mas o menos formalI en Haskell no es chequeada por el lenguaje
I ¿Como se utiliza el tipo?I a traves de sus operaciones y unicamente asıI no se puede usar pattern matching
138
Racionales como tipo abstractoI Recibimos operaciones, que no sabemos como estan
implementadas
crearR :: Int -> Int -> RacionalnumerR :: Racional -> IntdenomR :: Racional -> Int
I Y especificacion
tipo Racional {observador numerR(r : Racional): Int;observador denomR(r: Racional): Int;invariante denomR(r) > 0;invariante mcd(numerR(r), denom(R)) == 1;
}problema crearR(n, d: Int) = rac: Racional {
requiere d 6= 0;asegura numerR(rac) * d == denomR(rac) * n;
} 139
Racionales como tipo abstracto
I Numerador y denominador: forma normalizada (maximasimplificacion) con signo en el numerador
I No sabemos cuando se produce la simplificacion paranormalizar
I al construir el numero en crearRI al evaluarlo, en numerR y denomR
140
Definicion de nuevas operaciones
I Tal vez incluya operaciones basicasI Suma, multiplicacion, division
I Tambien podemos definirlas nosotros:
sumaR, multR, divR :: Racional -> Racional -> Racional
r1 ‘sumaR‘ r2 = crearR(denomR r2 * numerR r1 + denomR r1 * numerR r2)(denomR r1 * denomR r2)
r1 ‘multR‘ r2 = crearR(numerR r1 * numerR r2)(denomR r1 * denomR r2)
r1 ‘divR‘ r2 = crearR(denomR r2 * numerR r1)
(denomR r1 * numerR r2)
141
Otro ejemplo: Conjuntos
I Teorıa de conjuntosI Herramienta muy poderosa para el razonamiento matematico
I Pero el tipo de datos preferido para representar colecciones noes conjuntos, sino listas
I MotivoI Los conjuntos no pueden representarse con un tipo algebraico
que cumpla los criterios
I SolucionI Crear un tipo abstracto para los conjuntosI Las operaciones disponibles para el usuario van a limitar su uso
142
Operaciones de conjuntos
Vamos a definir conjuntos de enteros
I El conjunto vacıovacıo :: IntSet
I ¿El conjunto dado es vacıo?esVacıo :: IntSet -> Bool
I ¿Un elemento pertenece al conjunto?pertenece :: Int -> IntSet -> Bool
I Agregar un elemento al conjunto, si no estaba. Si estaba,dejarlo igual
agregar :: Int -> IntSet -> IntSet
I Elegir el menor numero y quitarlo del conjuntoelegir :: IntSet -> (Int, IntSet)
143
Transparencia referencial
I Las descripciones usan la metafora de la modificacionI Las operaciones no modifican los conjuntos, construyen
conjuntos nuevosI Pero es facil explicar las funciones en estos terminos
I elegir no quita nadaI El original, sigue igualI Devuelve un elemento y otro conjunto, con todos los
elementos del primero menos eseI No queda claro que pasa si el conjunto es vacıoI Se puede suponer que da error
I Estas descripciones no sustituyen a una especificacion
144
Nuevas operacionesI Definamos una operacion nueva: union
union :: IntSet -> IntSet -> IntSet
union p q | esVacıo p = qunion p q | otherwise = unionAux (elegir p) q
unionAux (x, p’) q = agregar x (union p’q)
I Pudimos hacerlo sin conocer la representacion de losconjuntos
I Usamos las operaciones provistasI Si cambia la representacion
I Habra que rescribir las ecuaciones para las operaciones del tipoabstracto (vacıo, esVacıo, pertenece, agregar,elegir)
I union y cualquier otra definida en otros programas quedanintactas
I La recursion no es privativa del pattern matchingI union esta definida en forma recursiva (a traves de unionAux)I Pero no usa recursion estructural ¡ni siquiera conocemos la
estructura! 145
Creacion de tipos abstractos
I Escribir un tipo abstracto que puedan usar otrosprogramadores
I En Haskell se hace con modulosI Archivos de texto que contienen parte de un programa
I Dos caracterısticas importantes en cualquier lenguajeI Encapsulamiento
I Agrupar estructura de datos con funciones basicasI Un programa no es una lista de definiciones y tipos, son
”capsulas”I Cada una con un (tal vez mas) tipo de datos y sus funciones
especıficas
I OcultamientoI Cuando se escribe un modulo se indica que nombres exporta
Cuales van a poder usarse desde afuera y cuales noI Aplicacion
Ocultar funciones auxiliares (como unionAux)Tambien para crear tipos de datos abstractos: Ocultar larepresentacion interna
146
Ejemplo de moduloI El primer ejemplo no es un tipo abstractoI Vamos a agrupar la funcionalidad de los complejosI Se representan bien mediante tipo algebraicoI Exportemos el tipo completo
module Complejos (Complejo(..), parteReal, parteIm) where
data Complejo = C Float Float
parteReal, parteIm:: Complejo -> Float
parteReal(C r i) = r
parteIm (C r i) = i
I module introduce el modulo: nombre, que exportaI Tipo(..) exporta el nombre del tipo y sus constructores
I Permite hacer pattern matching fuera del moduloI Despues del where van las definicionesI Si no hay lista de exportacion se exportan todos los nombres
definidosI module Complejos where...
147
Ejemplo de tipo abstracto
module Racionales (Racional, crearR, numerR, denomR)where
data Racional = R Int Int
crearR :: Int -> Int -> RacionalcrearR n d = reduce (n*signum d) (abs d)
reduce :: Int -> Int -> Racionalreduce x 0 = error "Racional con denom. 0"reduce x y = R (x ‘quot‘ d) (y ‘quot‘ d)
where d = gcd x y
numerR, denomR :: Racional -> Int
numerR (R n d) = n
denomR (R n d) = d
148
Aclaraciones
I signum (signo), abs (valor absoluto), quot (division entera) ygcd (gratest common divisor (MCD)) estan en el preludio
I En la lista de exportacion no dice (..) despues de RacionalI Se exporta solamente el nombre del tipoI NO sus constructoresI Convierte el tipo en abstracto para los que lo usen
I Tampoco exportamos reduce (auxiliar)
149
Uso del tipo
I Volvemos al rol de usuarioI Hay que indicar (en otro modulo) que queremos incorporar
este tipo de datosI Se usa la clausula import
I Todos los nombres exportados por un moduloI O solamente algunos de ellos (aclarando entre parentesis
cuales)
module Main whereimport Complejosimport Racionales (Racional, crearR)miPar :: (Complejo, Racional)miPar = (C 1 0, crearR 4 2)
I Las siguientes expresiones dan errorI numerR (snd miPar)I reduce 4 2I R 4 2 + R 2 1
150
Otra forma de crear tipos
I Vimos como crear sinonimos de tiposI Nombre adicional para un tipo existenteI Son intercambiables
I A veces, queremos un tipo nuevo con la representacion de unoexistente
I Que NO puedan intercambiarseI Por ejemplo, para un tipo abstracto
I Clausula newtype
151
newtype
Ejemplo: conjuntos
I Los representamos internamente con listas
I Encerramos la representacion en un tipo abstracto
newtype IntSet = Set [Int]
Diferencias con data
I Llama la atencion sobre renombreI A otro implementador encargado de modificarlaI A alguien que tenga que revisar el codigoI Al mismo programador dentro de un tiempo
I Admite un solo constructor con un parametroI No crea nuevos elementosI Renombra elementos existentes (no intercambiable)
I Mejor rendimiento
152
Implementacion de conjuntos
module ConjuntoInt (IntSet, vacıo, esVacıo, pertenece,agregar, elegir) where
import List (insert)
newtype IntSet = Set [Int]
vacıo :: IntSetvacıo = Set []
esVacıo :: IntSet -> BoolesVacıo (Set xs) = null xs
pertenece :: Int -> IntSet -> Boolpertenece x (Set xs) = x ‘elem‘ xs
agregar :: Int -> IntSet -> IntSetagregar x (Set xs) | elem x xs = Set xsagregar x (Set xs) | otherwise = Set (insert x xs)
elegir :: IntSet -> (Int, IntSet)
elegir (Set (x:xs)) = (x, Set xs)
153
Programacion funcionalClase 4
Terminacion
154
Causas de indefinicionLa evaluacion de una funcion puede no terminar (bien) porque:
1. se genera una cadena infinita de reducciones:
func1 :: Int -> Int -> Intfunc1 0 acum = acum
func1 i acum = func1 (i-1) (2*acum)
Si evaluamos func1 6 5 nos devuelve 320, pero la evaluacionde func1 (-6) 5 no termina.
2. ninguna lınea de la definicion de la funcion indica que hacer:
func2 :: [a] -> (a,a)func2 [x1,x2] = (x1,x2)
func2 (x:xs) = (x, snd (func2 xs))
Por ejemplo, func2 "algo1" = (’a’,’1’), pero laevaluacion de func2 "a" no termina.
3. invocamos a una funcion que no termina (bien)
No importa que Hugs nos devuelva el control en estos casos.Diremos que no termina o que la funcion se indefine.
155
Terminacion
Un programa (funcional) cumple con una especificacion cuandopara valores de entrada que satisfacen la precondicion:
1. el programa devuelve un valor definido (terminacion)
2. el valor devuelto satisface la poscondicion (correctitud)
I en esta clase vamos a aprender un metodo formal parademostrar que ciertos programas terminan
I en Algoritmos y Estructuras de Datos II van a estudiarmetodos para probar que los programas funcionales soncorrectos (induccion estructural)
Dada una funcion f y una precondicion Pf , diremos que f terminasi para cualesquiera valores de entrada x definidos que haganverdaderos a Pf , la evaluacion de f x termina.Aquı, x representa a todas las variables de entrada.
156
Demostracion de terminacion
Si miramos
func1 :: Int -> Int -> Intfunc1 0 acum = acum
func1 i acum = func1 (i-1) (2*acum)
puede parecer evidente que func1 termina si tomamosPfunc1 : i ≥ 0.
Veamos otro ejemplo:
func3 :: Int -> Boolfunc3 n | n == 1 = True
| n ‘mod‘ 2 == 0 = func3 (n ’div’ 2)
| otherwise = func3 (3*n + 1)
¿Es evidente que func3 termina cuando consideramosPfunc3 : x > 0?
157
Ecuaciones canonicasI en Haskell hay muchas maneras de definir funciones (guardas,
pattern matching, where, etc.)I queremos usar una serie de definiciones que nos permitan
analizar la funcion de manera homogeneaI para esto: ecuaciones canonicas
f x/(B1,C1,G1) = E1
f x/(B2,C2,G2) = E2
...
f x/(Bn,Cn,Gn) = En
I Gi es la guarda explıcita (en Haskell)I Bi es la guarda implıcita (termino de tipo Bool del lenguaje de
especificacion). ¿Por que entra por la i-esima ecuacion?I Ci es el contexto de evaluacion (termino de tipo Bool; escrito
en lenguaje de especificacion) que representa informacionadicional proveniente de pattern matching o clausula where.
I Ei es la expresion del lado derecho (en Haskell).158
Ejemplo
problema suma(l : [Int]) = res : Int{asegura : res ==
∑l ;
}
suma :: [Int] -> Intsuma [] = 0
suma (x:xs) = x + suma xs
Las ecuaciones canonicas son:
suma l / (|l | == 0, true , True) = 0suma l / (|l | > 0 , x == cab(l) ∧ xs == cola(l), True) = x + suma xs
o, alternativamente,
suma l / (|l | == 0, true , True) = 0suma l / (|l | > 0 , x == l [0] ∧ xs == l [1..|l |], True) = x + suma xs
159
Hipotesis de la demostracion
Asumimos que:
1. esta demostrado que cada funcion g que aparece en algunaguarda o where termina si se utiliza cierta precondicion(conocida) Pg
2. esta demostrado que cada funcion g 6= f que aparece en algunlado derecho termina si se utiliza cierta precondicion(conocida) Pg
Quedan fuera de este esquema de demostracion las funcionesmutuamente recursivas.
160
Reglas de terminacion
Daremos 5 reglas sobre ecuaciones canonicas de una cierta funcionf: R1, R2, R3, R4 y R5.
I si se pueden probar las 5 reglas, entonces f termina
I las reglas solo predican sobre las ecuaciones canonicas; nosolvidamos de las ecuaciones originales
I si no podemos probar alguna de las 5 reglas no quiere decirque la funcion no termine; las 5 reglas son condicionessuficientes pero no necesarias
161
R1 - las funciones se evaluan dentro de su precondicionproblema prLL(l : [[Int]]) = res : [R]{asegura res ==[if |y | > 0 then prom(y) else 0 | y ← l ]}
problema prom(l : [Int]) = res : R{requiere |l | > 0;asegura res == prom(l); }aux prom(l : [Int]) : Float =
∑l/|l |
prLL :: [[Int]] -> [Float]prLL [] = []prLL ([]:xs) = 0 : (prLL xs)prLL (x:xs) = (prom x) : (prLL xs)
prLL l / (|l | == 0 , true , True) = []
prLL l / (|l | > 0 ∧ l [0] == [ ], xs == l [1..|l |) , True) = 0 : (prLL xs)
prLL l / (|l | > 0 , l [0] == x ∧ xs == l [1..|l |), True) = (prom x):(prLL xs)
R1 sea i entre 1 y n, y sea g y (g puede ser f) una expresion queaparece en Gi o en Ei . Si
I x hace verdadero Pf ,I valen ¬B1, . . . ,¬Bi−1,I si g y aparece en Ei vale ademas Bi ,
entonces y debe hacer verdadero Pg .
En el ejemplo, prom siempre se invoca con argumentos que satisfacen Pprom.162
R2 - hay suficientes ecuaciones
problema diferenciaAbsoluta(a, b : Int) = d : Int{asegura : d == |a− b|;
}diferenciaAbsoluta :: Int -> Int -> IntdiferenciaAbsoluta a b | a > b = a - b
| a < b = b - a
Aquı hay un problema porque falta analizar el caso a == b.Deberıa ser, por ejemplo:
diferenciaAbsoluta :: Int -> Int -> IntdiferenciaAbsoluta a b | a > b = a - b
| a < b = b - a| a == b = 0
R2 si x estan definidas y hacen verdadero Pf , entonces existe un ientre 1 y n tal que x hacen verdadero Bi .
163
R3, R4 y R5 - no hay reducciones infintas
problema factorial(n : Int) = fact : Int{asegura : fact ==
∏[1..n];
}factorial :: Int -> Int -> Intfactorial n | n == 0 = 1
| otherwise = n * factorial (n-1)
Si evaluamos factorial 5 obtenemos 120. Pero al evaluarfactorial (-1) se genera una cadena infinita de reducciones
Idea:
I garantizar que en cada llamado recursivo nos “acercamos”masal caso base
I dar una forma numerica de medir que tan cerca del caso baseestamos
I esa forma numerica es la funcion variante, que sera untermino de tipo Int de nuestro lenguaje de especificacion
164
R3, R4 y R5 - no hay reducciones infintas
f x/(B1,C1,G1) = E1
f x/(B2,C2,G2) = E2
...
f x/(Bn,Cn,Gn) = En
Proponemos una funcion variante Fv (x) tal que:
R3 si x estan definidas y hacen verdadero Pf , entonces Fv (x) nose indefine.
R4 sea i entre 1 y n, con Ei un caso recursivo, y x tales que secumple (Pf ∧ ¬B1 ∧ . . . ∧ ¬Bi−1 ∧ Bi ). Entonces, para cadaaparicion de f y en Ei , vale que Fv (x) > Fv (y).
R5 existe una constante entera k, que denominaremos cota deFv , con la siguiente propiedad: toda vez que x hace verdaderoPf y Fv (x) ≤ k , debe existir i entre 1 y n tal que
I Ei es un caso base yI vale (¬B1 ∧ . . . ∧ ¬Bi−1 ∧ Bi ).
165
Resumiendo
Condiciones basicas (y faciles de demostrar)
R1 todas las funciones se evaluan dentro de su precondicion
R2 hay suficientes ecuaciones (no faltan casos)
Debemos proponer una funcion variante Fv (x) y una cota tal que(esto es mas difıcil de demostrar):
R3 Fv (x) no se indefine (si vale Pf )
R4 Fv (x) es decreciente
R5 si Fv (x) pasa la cota entonces f entra a un caso base (y porlo tanto termina)
166
ultimo - ejemplo completo
problema ultimo(l : [T ]) = u : T{requiere : |l | > 0;asegura : u == l [|l | − 1];
}
ultimo :: [a] -> aultimo [x] = x
ultimo (x:xs) = ultimo xs
Las ecuaciones canonicas son:
ultimo l / (|l | == 1, x == l [0] , True) = xultimo l / (|l | > 0 , x == l [0] ∧ xs == l [1..|l |), True) = ultimo xs
167
ultimo - R1ultimo l / (|l | == 1, x == l [0] , True) = xultimo l / (|l | > 0 , x == l [0] ∧ xs == l [1..|l |), True) = ultimo xs
R1 sea i entre 1 y n, y sea g y (g puede ser f) una expresion queaparece en Gi o en Ei . Si
I x hace verdadero Pf ,I valen ¬B1, . . . ,¬Bi−1,I si g y aparece en Ei vale ademas Bi ,
entonces y debe hacer verdadero Pg .
La unica funcion utilizada es ultimo, en E2. Tenemos que ver que si:
a) l satisface Pultimo, es decir |l | > 0,
b) vale ¬B1, esto es, ¬|l | == 1, y
c) vale ademas B2, es decir |l | > 0,
entonces xs satisface Pultimo, o sea: |xs| > 0.
I de a) y b) sabemos que |l | ≥ 2
I de C2 sabemos xs == l [1..|l |); entonces |xs| == |l | − 1
I concluimos |xs| > 0 168
ultimo - R2
ultimo l / (|l | == 1, x == l [0] , True) = xultimo l / (|l | > 0 , x == l [0] ∧ xs == l [1..|l |), True) = ultimo xs
R2 si x estan definidas y hacen verdadero Pf , entonces existe un ientre 1 y n tal que x hacen verdadero Bi .
Es claro que B2 cumple la regla.
169
ultimo - R3
ultimo l / (|l | == 1, x == l [0] , True) = xultimo l / (|l | > 0 , x == l [0] ∧ xs == l [1..|l |), True) = ultimo xs
Proponemos Fv (l) = |l | y cota k = 1.
R3 si x estan definidas y hacen verdadero Pf , entonces Fv (x) noesta indefinido.
La operacion de longitud es una funcion total sobre listas. Es decir,no se indefine para ningun valor definido.
170
ultimo - R4
ultimo l / (|l | == 1, x == l [0] , True) = xultimo l / (|l | > 0 , x == l [0] ∧ xs == l [1..|l |), True) = ultimo xs
R4 sea i entre 1 y n, con Ei un caso recursivo, y x tales que secumple (¬B1 ∧ . . . ∧ ¬Bi−1 ∧ Bi ). Entonces, para cadaaparicion de f y en Ei , vale que Fv (x) > Fv (y).
I solo tenemos un caso recursivo, que corresponde a i = 2I supongamos que l cumple (Pultimo ∧ ¬B1 ∧ B2). Debemos
comprobar que vale
Fv (l) > Fv (xs)
I por (el contexto de) B2, sabemos que xs == l [1..|l |), con locual |xs| == |l | − 1. Entonces
Fv (l) == |l | > |l | − 1 == |xs| == Fv (xs).171
ultimo - R5
ultimo l / (|l | == 1, x == l [0] , True) = xultimo l / (|l | > 0 , x == l [0] ∧ xs == l [1..|l |), True) = ultimo xs
R5 toda vez que x hace verdadero Pf y Fv (x) ≤ k, debe existir ientre 1 y n tal que Ei es un caso base, y vale(¬B1 ∧ . . . ∧ ¬Bi−1 ∧ Bi ).
Debemos garantizar la entrada a un caso base cuando Fv esta pordebajo de la cota.
I supongamos l tal que valen Pultimo y Fv (l) ≤ k
I entonces |l | > 0 y |l | ≤ 1
I como |l | es un entero, debe ser necesariamente |l | == 1
I esto coincide con B1, que es la guarda correspondiente al casobase (i = 1).
172
Entonces ultimo termina
Como pudimos probar las reglas R1, R2, R3, R4 y R5. concluimosque ultimo termina.
Observar que:
I se debe proponer una funcion variante Fv y una cota k paraprobar R3, R4 y R5
I esa funcion variante y cota deben ser las mismas para probarR3, R4 y R5
I hubiera servido una cota menor, por ejemplo, k = 0 ok = −1. ¿Por que?
173
mezclarOrdenado - ejemplo completo
problema mezclarOrdenado(l1, l2 : [Int]) = r : [Int]{requiere : ordenado(l1) ∧ ordenado(l2);asegura : ordenado(r) ∧mismos(r , l1 + +l2);
} aux ordenado(l : [T ]) : Bool = (∀i ← [0..|l | − 1)) l [i ] ≤ l [i + 1]
mezOrd :: [Int] -> [Int] -> [Int]mezOrd [] l2 = l2mezOrd l1 [] = l1mezOrd (x:xs) (y:ys) | x<=y = x : (mezOrd xs (y:ys))
mezOrd (x:xs) (y:ys) | otherwise = y : (mezOrd (x:xs) ys)
mezOrd l1 l2 / (|l1| == 0, true, True) = l2mezOrd l1 l2 / (|l2| == 0, true, True) = l1mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0 ∧ x ≤ y ,
x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),x<=y) = x : (mezOrd xs (y:ys))
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),
True) = y : (mezOrd (x:xs) ys) 174
mezclarOrdenado - R1
mezOrd l1 l2 / (|l1| == 0, true, True) = l2
mezOrd l1 l2 / (|l2| == 0, true, True) = l1
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0 ∧ x ≤ y ,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),x<=y) = x : (mezOrd xs (y:ys))
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),True) = y : (mezOrd (x:xs) ys)
R1 sea i entre 1 y n, y sea g y (g puede ser f) una expresion queaparece en Gi o en Ei . Si
I x hace verdadero Pf ,I valen ¬B1, . . . ,¬Bi−1,I si g y aparece en Ei vale ademas Bi ,
entonces y debe hacer verdadero Pg .
Observar que mezord siempre se llama con parametros que verifican la
precondicion (las dos listas que recibe estan ordenadas).
175
mezclarOrdenado - R2
mezOrd l1 l2 / (|l1| == 0, true, True) = l2
mezOrd l1 l2 / (|l2| == 0, true, True) = l1
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0 ∧ x ≤ y ,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),x<=y) = x : (mezOrd xs (y:ys))
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),True) = y : (mezOrd (x:xs) ys)
R2 si x estan definidas y hacen verdadero Pf , entonces existe un i entre1 y n tal que x hacen verdadero Bi .
Observar que siempre ocurre que
|l1| = 0 ∨ |l2| = 0 ∨ (|l1| > 0 ∧ |l2| > 0)
176
mezclarOrdenado - Funcion variante y cotamezOrd l1 l2 / (|l1| == 0, true, True) = l2
mezOrd l1 l2 / (|l2| == 0, true, True) = l1
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0 ∧ x ≤ y ,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),x<=y) = x : (mezOrd xs (y:ys))
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),True) = y : (mezOrd (x:xs) ys)
Posibilidades:
I Fv(l1, l2) == |l1|I en E3 decreceI pero no sirve porque en E4 no decrece
I Fv(l1, l2) == |l2|I en E4 decreceI pero no sirve porque en E3 no decrece
I Fv(l1, l2) == |l1|+ |l2| es un buen candidato
I proponemos cota k = 0177
mezclarOrdenado - R3
mezOrd l1 l2 / (|l1| == 0, true, True) = l2
mezOrd l1 l2 / (|l2| == 0, true, True) = l1
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0 ∧ x ≤ y ,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),x<=y) = x : (mezOrd xs (y:ys))
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),True) = y : (mezOrd (x:xs) ys)
R3 si x estan definidas y hacen verdadero Pf , entonces Fv (x) noesta indefinido.
Fv(l1, l2) == |l1|+ |l2|
La operacion de longitud y la suma son funciones totales.
178
mezclarOrdenado - R4mezOrd l1 l2 / (|l1| == 0, true, True) = l2
mezOrd l1 l2 / (|l2| == 0, true, True) = l1
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0 ∧ x ≤ y ,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),x<=y) = x : (mezOrd xs (y:ys))
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),True) = y : (mezOrd (x:xs) ys)
R4 sea i entre 1 y n, con Ei un caso recursivo, y x tales que secumple (Pf ∧ ¬B1 ∧ . . . ∧ ¬Bi−1 ∧ Bi ). Entonces, para cadaaparicion de f y en Ei , vale que Fv (x) > Fv (y).
I hay dos casos recursivos, i = 3 e i = 4I para E3, tenemos
Fv(xs, l2) == |xs|+ |l2| < |l1|+ |l2| = Fv(l1, l2)
I para E4, tenemos
Fv(l1, ys) == |l1|+ |ys| < |l1|+ |l2| = Fv(l1, l2)179
mezclarOrdenado - R5mezOrd l1 l2 / (|l1| == 0, true, True) = l2
mezOrd l1 l2 / (|l2| == 0, true, True) = l1
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0 ∧ x ≤ y ,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),x<=y) = x : (mezOrd xs (y:ys))
mezOrd l1 l2 / (|l1| > 0 ∧ |l2| > 0,x == l1[0] ∧ xs == cola(l1) ∧ y == l2[0] ∧ ys == cola(l2),True) = y : (mezOrd (x:xs) ys)
R5 toda vez que x hace verdadero Pf y Fv (x) ≤ k, debe existir ientre 1 y n tal que Ei es un caso base, y vale(¬B1 ∧ . . . ∧ ¬Bi−1 ∧ Bi ).
Debemos garantizar la entrada a un caso base cuandoFv (l1, l2) ≤ 0.
I supongamos Fv(l1, l2) = |l1|+ |l2| ≤ 0I entonces |l1| == 0 y |l2| == 0I en particular, vale B1 (tambien vale B2)
180
Programacion funcionalClase 5
Correctitud - ordenes de evaluacion - alto orden
181
Correctitud
I ya vimos como demostrar terminacion
I para demostrar que una definicion es correcta, hace falta
I una especificacionproblema fact(n : Int) = result : Int{
requiere n ≥ 0;asegura result ==
∏[1..n];
}I y un programa
fact 0 = 1fact n | n > 0 = n * fact (n-1)
I definicion recursiva: es natural demostrar por induccion
182
Demostracion de correctitudQuiero probar que para todo x , la funcion fact con entrada xdevuelve
∏[1..x ].
I caso base (n == 0)I la especificacion dice que hay que probar que la funcion fact
con entrada 0 devuelve∏
[1..0] ==∏
[ ] = 1I pero eso es justamente lo que devuelve fact en el caso base
I hipotesis inductiva (HI): fact n ==∏
[1..n]I la especificacion dice que hay que probar que la funcion fact
con entrada n + 1 devuelve∏
[1..n + 1]I calculemos fact n + 1I gracias a la precondicion, n + 1 > 0. Entonces uso la segunda
ecuacion instanciando en n + 1
fact(n + 1) == (n + 1) ∗ fact(n)
I usando HI:
fact(n + 1) == (n + 1) ∗∏
[1..n]
==∏
[1..(n + 1)]
I entonces, demostre que fact con entrada n + 1 devuelve∏[1..(n + 1)] 183
Principio de induccion para NI sea P(x) una propiedad de x ∈ NI supongamos que queremos probar que vale P(x) para todo
x ∈ NI el axioma de induccion dice:
I siI P(0) es verdadero yI para todo n, P(n)→ P(n + 1) es verdadero
I entonces P(x) es verdadero para todo x ∈ NI el axioma de induccion completa dice:
I siI P(0) es verdadero yI para todo n, (∀m ≤ n P(m))→ P(n + 1) es verdadero
I entonces P(x) es verdadero para todo x ∈ NI en el ejemplo anterior, P(x) dice
“la funcion fact con entrada x devuelve∏
[1..x ]”
184
Otro ejemplo de demostracion de correctitudI vimos como demostrar la correctitud de fact(n) haciendo induccion en el
parametro de entrada (es decir, en n)I tambien se puede hacer induccion en el valor que toma una funcionI por ejemplo,
problema suma(l : [Int]) = result : Int{asegura result ==
∑l ;
}
suma [] = 0suma (x:xs) = x + suma xs
I quiero probar P(l) :“la salida de suma con entrada l es∑
l”I se puede hacer induccion en |l | (que toma valores en N)
I caso base: pruebo P(l) para l tal que |l | == 0.Es decir, tengo que probar P(l) para l == [ ].suma con entrada [ ] devuelve 0 = |l |.
I HI: vale P(l) para toda l tal que |l | ≤ n (n ∈ N)Quiero ver que vale P(l) para toda l tal que |l | == n + 1.Sea y tal que |y | == n + 1. Quiero ver que vale P(y).Como |y | == n + 1, sabemos que y == x : xs para alguna lista xs y entero x .La salida de suma con entrada y es x + salida de suma con entrada xs.Uso la HI (notar que |xs| ≤ n): la salida de suma con entrada y es x +
∑xs.
Por propiedad de∑
, x +∑
xs ==∑
(x : xs) ==∑
y .Entonces la salida de suma con entrada y es
∑y .
Es decir, vale P(y). 185
Principio de induccion para tipos algebraicosI se llama induccion estructural. La van a estudiar en Algoritmos y
Estructuras de Datos II
I sea P(x) una propiedad de un elemento x de un tipo algebraico T
I supongamos que queremos probar que vale P(x) para todo x de tipo T
I sea c una funcion matematica T → N
I el axioma de induccion dice:I si
I P(y) es verdadero para toda y tal que c(y) = 0 yI para todo y , si ∀y (c(y) = n→ P(y)) entonces ∀y (c(y) = n + 1→ P(y))
I entonces P(y) es verdadero para todo y de tipo T
I el axioma de induccion completa dice:I si
I P(y) es verdadero para toda y tal que c(y) = 0 yI para todo y , si ∀y (c(y) ≤ n→ P(y)) entonces ∀y (c(y) = n + 1→ P(y))
I entonces P(y) es verdadero para todo y de tipo T
I en el ejemplo anterior T es el tipo [Int] yI P(y) dice “la funcion suma con entrada y devuelve
∑y”
I c(y) = |y |. 186
Reduccion
Modelo de computo:
I como se calcula el valor de una expresionI puede afectar la semantica
I distintos modelos pueden dar distintos resultados
Reduccion:
I mecanismo de evaluacion en HaskellI reemplazar una subexpresion por otra
I reemplazada:I instancia del lado izquierdo de una ecuacion orientadaI se llama redex (reducible expression) o radical
I reemplazante:I lado derecho, instanciado de manera acordeI el resto de la expresion queda igual
I instanciacionI asignacion de expresiones a variables de un pattern
187
Ejemplo
I expresionsuma (restar 2 (amigos Juan)) 4
I ecuacionrestar x y = x - y
I reduccion
1. busco un redex y asignacionsuma (restar 2 (amigos Juan)︸ ︷︷ ︸
redex
) 4
I asignacion:x ← 2
y ← (amigos Juan)
2. reemplazo el redex con esa asignacion
suma (restar 2 (amigos Juan)) 4 suma (2 - (amigos Juan)) 4
188
Formas normales
I valor de una expresionI sigo reduciendo hasta que no haya mas redexes
I obtengo una forma normalI solamente constantes y constructores
I son formas normalesI 2I (3, True, C 5.1 (-6.2))
I no son formas normalesI 1 + 1I (9 ‘quot‘ 3, 3>=2, sumarC (C 1.05 (-12.4)) (C 4.05
(6.2)))
189
Mecanismo de reduccion
1. si la expresion esta en forma normal, terminamos
2. si no, buscar un redex
3. reemplazarlo
4. volver a empezar
I todos los mecanismos de reduccion comparten este algoritmoI las estrategias dependen del paso 2
I en que orden elijo los redexesI se las llama ordenes de reduccion
I pueden influir en que se llegue o no a una forma normal
190
Normalizacion
I ¿toda expresion tiene forma normal?I no. Las siguientes expresiones no tienen forma normal
I f x = f (f x) ¿cuanto vale f 3?I infinito = infinito + 1 ¿cuanto vale infinito?I inverso x | x /= 0 = 1 / x ¿cuanto vale inverso 0?
I si se consigue, ¿toda estrategia encuentra la misma?I sıI se llama confluencia
191
Bottom
I las expresiones que no tienen forma normal se llamanindefinidas
I se puede decir que su valor es ⊥I podrıamos definirlo en Haskell
bottom :: abottom = bottom
I cualquier intento de evaluar bottom se indefineg :: Int -> Intg x = if x == bottom then 1 else 0g 2 ⊥
192
IndefinicionI le pasamos un valor definido a una funcion
I parciales: a veces devuelven ⊥I totales: nunca
I le pasamos ⊥ a una funcionI ¿devuelve ⊥?
I no siempreI depende de sus ecuaciones y del orden de reduccion
I estrictas: f⊥ ⊥I no estrictas: f⊥ valor
Totales vs. parciales
I totalsuc :: Integer -> Integer
suc x = x + 1
I parcialesrecip :: Float -> Float
recip x | x /= 0 = 1/x
I las dos son estrictasI si les paso ⊥ devuelven ⊥
Estrictas vs. no estrictas
I ecuacionesconst :: a -> b -> a
const x y = x
I ¿cuanto vale?I const 2 infinito
Depende del diseno del lenguaje. Elsecreto esta en el orden de evaluacion
193
Orden de evaluacion
I forma de elegir el proximo redexI recordar confluencia
I si por dos ordenes llegamos a valores definidos, es el mismovalor
I pero puede ser que un orden llegue a ⊥ y otro no
infinito :: Integer -> Integer
infinito = infinito + 1
const :: a -> b -> a
const x y = x
const 2 infinito
2 const 2 (infinito+1)
2 const 2 ((infinito+1)+1)
2 . . .
194
Ordenes de evaluacion
I aplicativoI primero redexes internosI primero los argumentos, despues la aplicacion
I normalI el redex mas externo
I para el que pueda hacer pattern matching
I primero la aplicacion, despues los argumentosI si se necesitan
I siempre encuentra la forma normalI si la hay
I los dos empiezan a izquierdaI en caso de mas de un redex del mismo nivel
195
Ejemplos de evaluacion aplicativa
f x = 0
f (1/0) se indefine
————————————————————————————
infinito :: Integer -> Integer
infinito = infinito + 1
const :: a -> b -> a
const x y = x
const 2 infinito const 2 (infinito+1) const 2 ((infinito+1)+1) const 2 (((infinito+1)+1)+1) . . .
————————————————————————————
head :: [a] -> a
head (x:xs) = x
tail :: [a] -> a
tail (x:xs) = xs
inc :: [a] -> a
inc [] = []
inc (x:xs) = (x+1):inc xs
head (tail (inc [1,2,3,4])) head (tail (2:inc [2,3,4])) head (tail (2:3:inc [3,4])) head (tail (2:3:4:inc [4])) head (tail (2:3:4:5:inc [])) head (tail (2:3:4:5:[])) head (3:4:5:[]) 3
196
Ejemplos de evaluacion normal
f x = 0
f (1/0) 0
————————————————————————————
infinito :: Integer -> Integer
infinito = infinito + 1
const :: a -> b -> a
const x y = x
const 2 infinito 2
————————————————————————————
head :: [a] -> a
head (x:xs) = x
tail :: [a] -> a
tail (x:xs) = xs
inc :: [a] -> a
inc [] = []
inc (x:xs) = (x+1):inc xs
head (tail (inc [1,2,3,4])) head (tail (2:inc [2,3,4])) head (inc [2,3,4]) head (3:inc [3,4]) 3
197
Propiedades de los ordenes
I orden aplicativoI const es estrictaI todas son estrictas
I orden normalI const es no estrictaI hay funciones estrictas y no estrictasI depende de si necesitan evaluar todos sus argumentos
198
Ordenes y performance
fib 1 = 1fib 2 = 1fib n | n > 2 = fib (n-1) + fib (n-2)
¿Cuantas reducciones necesito?
I fib 20 → 200.000I const 3 (fib 20) → ?
I aplicativo: 200.001I normal: 1
I quin x = x + x + x + x + xquin (fib 20) → ?
I aplicativo: 200.005I normal: (fib 20) + (fib 20) + (fib 20) + (fib 20) + (fib 20) →
1.000.000
199
Evaluacion lazy
I perezosa, haragana, fiaca
I algoritmo usado en HaskellI orden normal, pero...
I aprovecha la transparencia referencialI si una expresion vuelve a aparecer, se acuerda el valor anteriorI quin (fib 20) → 200.005
200
Estructuras infinitas
I es una ventaja de la evaluacion lazyI ejemplos
I naturales, pares, impares :: [Integer]naturales = secuencia 0 1impares = secuencia 1 2pares = secuencia 0 2
secuencia :: Integer -> Integer -> [Integer]secuencia n p = n:secuencia (n+p) p
I evaluacion de estructuras infinitasI head naturales head (secuencia 0 1)
head (0:secuencia 1 1) 0I take 10 impares · · · [1,3,5,7,9,11,13,15,17,19]
201
Alto ordenI filtrar los elementos mayores a 2 de una lista:
filtroMayoresADos :: [Int] -> [Int]
filtroMayoresADos [] = []
filtroMayoresADos (x:xs) | x>2 = x:filtroMayoresADos xs
| otherwise = filtroMayoresADos xs
I filtrar los elementos pares de una lista:filtroPares :: [Int] -> [Int]
filtroPares [] = []
filtroPares (x:xs) | (x ‘mod‘ 2==0) = x:filtroPares xs
| otherwise = filtroPares xs
I mas general:filter: (Int -> Bool) -> [Int] -> [Int]
filter f [] = []
filter f (x:xs) | f x = x:filter f xs
| otherwise = filter f xs
I filtroMayoresADos xs = filter mayorADos xs
where mayorADos x = x>2I filtroPares xs = filter par xs
where par x = (x ‘mod‘ 2 == 0)
I las funciones de alto orden son aquellas que reciben odevuelven funciones 202
Alto ordenI sumar los elementos de una lista
suma [] = 0
suma (n:ns) = n + suma ns
I verificar que sean todos verdaderosall [] = True
all (b:bs) = b && all bs
I aplanar una lista de listasconcat [] = []
concat (xs:xss) = xs ++ concat xss
I mas general:foldr f a [] = a
foldr f a (x:xs) = x ‘f‘ (foldr f a xs)
I suma = foldr (+) 0I all = foldr (&&) TrueI concat = foldr (++) []
I ¿cual es el tipo de foldr?I de los ejemplos se deduce (a -> a -> a) -> a -> [a] -> a
I pero puede ser un poco mas general:(a -> b -> b) -> b -> [a] -> b
203
Currificacion
I suma’ :: (Int , Int) -> Intsuma’ (x,y) = x + y
I recibe un par ordenado
I suma :: Int -> Int -> Intsuma x y = x + y
I el tipo de suma se interpreta comosuma :: Int -> (Int -> Int)
I es una funcion que recibe un entero y devuelve una funcion deenteros en enteros
I ¿que es suma 1?I una funcion de tipo Int -> IntI (suma 1) y = suma 1 y = 1+y
I ¿que es suma 23?I tambien es una funcion de tipo Int -> IntI (suma 23) y = suma 23 y = 23+y
I en general suma x y = (suma x) y = x+y
204
Programacion imperativaClase 1
Introduccion a la programacion imperativa
205
Programacion imperativa (diferencias con funcional)I los programas no necesariamente son funciones
I ahora pueden devolver mas de un valorI hay nuevas formas de pasar argumentos
I nuevo concepto de variablesI posiciones de memoriaI cambian explıcitamente de valor a lo largo de la ejecucion de
un programaI perdida de la transparencia referencial
I nueva operacion: la asignacionI cambiar el valor de una variable
I las funciones no pertenecen a un tipo de datosI distinto mecanismo de repeticion
I en lugar de la recursion usamos la iteracionI nuevo tipo de datos: el arreglo
I secuencia de valores de un tipo (como las listas)I longitud prefijadaI acceso directo a una posicion (en las listas, hay que acceder
primero a las anteriores)
206
Lenguaje C++
I vamos a usarlo para la programacion imperativa (tambiensoporta parte del paradigma de objetos)
I vamos a usar un subconjunto (como hicimos con Haskell)I no objetos, no memoria dinamica, etc.I sı vamos a usar la notacion de clases, para definir tipos de
datos
I el tipado es mas debil que el de HaskellI tipos basicos:
I charI floatI int
(con minuscula)
207
Programa en C++
I coleccion de tipos y funciones
I definicion de funcion
tipoResultado nombreFuncion (parametros)bloqueInstrucciones
I ejemplo
int suma2 (int x, int y) {int res = x + y;return res;
}
I su evaluacion consiste en ejecutar una por una lasinstrucciones del bloque
I el orden entre las instrucciones es importanteI siempre de arriba hacia abajo
208
Variables en imperativo
I nombre asociado a un espacio de memoria
I puede cambiar de valor varias veces en la ejecucionI en C++ se declaran dando su tipo y su nombre
I int x; −→ x es una variable de tipo intI char c; −→ c es una variable de tipo char
I programacion imperativaI conjunto de variablesI instrucciones que van cambiando sus valoresI los valores finales, deberıan resolver el problema
209
La asignacionI operacion fundamental para modificar el valor de una variableI sintaxis
variable = expresion;
I operacion asimetricaI lado izquierdo: debe ir una variable u otra expresion que
represente una posicion de memoriaI lado derecho: puede ser una expresion del mismo tipo que la
variableI constanteI variableI funcion aplicada a argumentos
I efecto de la asignacion1. se evalua el valor de la expresion de la derecha2. ese valor se copia en el espacio de memoria de la variable3. el resto de la memoria no cambia
I ejemplos: x = 0; y = x; x = x+x;
x = suma2(z+1,3); x = x*x + 2*y + z;I no son asignaciones:
3 = x; doble (x) = y; 8*x = 8;210
La instruccion return
I termina la ejecucion de una funcion
I retorna el control a su invocador
I devuelve la expresion como resultado
int suma2 (int x, int y) {int res = x + y;return res;
}
int suma2 (int x, int y) {return x + y;
}
211
Transformacion de estados
I llamamos estado de un programa a los valores de todas susvariables en un punto de su ejecucion
I antes de ejecutar la primera instruccionI entre dos instruccionesI despues de ejecutar la ultima instruccion
I podemos ver una ejecucion de un programa como unasucesion de estados
I la asignacion es la instruccion que transforma estadosI el resto de las instrucciones son de control
I modifican el flujo de ejecucionI orden de ejecucion de las instrucciones
212
Afirmaciones en imperativoI al demostrar una propiedad, agregamos afirmaciones sobre el
estado entre instruccionesI se amplıa el lenguaje de especificacion con la sentencia vale
vale nombre: P;
I el nombre (nombre) es opcionalI P es un predicado
I expresion de tipo Bool del lenguaje de especificacionI se coloca entre dos instruccionesI significa que P vale en ese punto del programa en cualquier
ejecucionI el compilador no entiende las sentencias del lenguaje de
especificacionI para que no de error, las ponemos en comentarios
I como en Haskell, pero con otra sintaxisI hay dos maneras:
I // comenta una sola lıneaI /*...*/ comenta varias lıneas
213
Clausulas vale y estado
I ejemplo de codigocon afirmaciones
x = 0;//vale x == 0;
x = x + 3;//vale x == 3;
x = 2 * x;//vale x == 6;
I para referirnos al valor que tenıa en un estadoanterior
I usamos estado para dar un nombre al estado
I nos referimos al estado anterior con
variable@nombreEstado
I semantica de la asignacion:
//estado av = e;
//vale v == e@a
I despues de ejecutar la asignacion, la variable vtiene el valor que tenıa antes la expresion e
I en cada ejecucion el estado puede ser distinto
214
Clausula preI el operador pre se refiere al estado previo a la ejecucion de
una funcion
pre(expresion)
I representa el valor de la expresion en el estado inicialI cuando los parametros de una funcion reciben el valor de los
argumentos con los que se la invoco
problema suc(x : Int) = res : Int {asegura res == x + 1; }
int suc(int x) {int y = x;
//estado a//vale y == x ;
y = y + 1;//vale y == y@a + 1;
return y;}
int suc(int x) {//local x//vale x == pre(x)
x = x + 1;//vale x == pre(x) + 1;
return x;}
215
Clausula localI en las afirmaciones, se presupone que los parametros mantienen su valor inicialI en cualquier estado n
vale parametro@n == pre(parametro)
I evita tener que repetir esta afirmacion en cada estado, exceptoI parametros que aparecen en clausulas modifica de la especificacionI si queremos usar un parametro como variable temporaria, usamos la
clausula local
problema suma1(x , y : Int) {modifica x ;asegura x == pre(x) + y ; }
void suma1(int &x, int y) {//estado a//vale x == pre(x);//vale y == pre(y); (no hace falta)
x = x + y;//vale x == pre(x) + y ;//vale y == pre(y); (no hace falta)
}
problema suma2(x , y : Int) = res : Int {asegura res == x + y ; }
int suma2(int x, int y) {//local x//vale x == pre(x);//vale y == pre(y); (no hace falta)
x = x + y;//vale x == pre(x) + y ;//vale y == pre(y); (no hace falta)
return x;}
216
Clausula implicaI clausula
implica P;
I se pone despues de una o mas afirmaciones vale o implicaI indica que P se deduce de las afirmaciones anteriores
problema suc(x : Int) = res : Int {asegura res == x + 1; }
¡todas las clausulas implica deben serjustificadas!
I en el ejemplo,I //implica x == pre(x) + 2− 1: sale
de reemplazar x@a por pre(x) + 2,de acuerdo a lo que dice el estado a
I //implica x == pre(x) + 1:operaciones aritmeticas
I //implica res == pre(x) + 1: sale dereemplazar x@b por pre(x) + 1, deacuerdo a lo que dice el estado b
int suc(int x) {//local x
x = x + 2;//estado a//vale x == pre(x) + 2;
x = x - 1;//estado b//vale x == x@a− 1;//implica x == pre(x) + 2− 1;//implica x == pre(x) + 1;
return x;//vale res == x@b;//implica res == pre(x) + 1;
} 217
Pasaje de argumentos
I los argumentos tienen que pasar del invocador al invocado
I hay que establecer una relacion entre argumentos y parametrosI las convenciones mas habituales son dos
I por valorI antes de hacer la llamada se obtiene el valor del argumentoI se lo coloca en la posicion de memoria del parametro correspondienteI durante la ejecucion de la funcion, se usan esas copias de los
argumentosI por eso tambien se llama por copiaI los valores originales quedan protegidos contra escritura
I por referenciaI en la memoria asociada a un parametro se almacena la direccion de
memoria del argumento correspondienteI el parametro se convierte en una referencia indirecta al argumentoI todas las asignaciones al parametro afectan directamente el valor de la
variable pasada como argumentoI el argumento no puede ser una expresion cualquiera (por ejemplo, un
literal) debe indicar un espacio de memoria (en general, es una variable)
218
Referencias en C++
I referencia a una variable
I constructor de tipos &
I actua como un alias (se usa sin sintaxis especial, como el tipooriginal)
int &b = a;b = 3;
//vale a == b == 3;a = 4;
//vale a == b == 4;
I complica la comprension del programa y su semantica
I por eso pedimos que una posicion de memoria no tenga dosnombres
219
Pasaje de argumentos en C++I siempre por valorI para pasar por referencia, se usan referenciasI tambien se pasan por valor (copia), pero lo que se copia no es
el valor del argumento, se copia su direccionI la modificacion del parametro implica modificacion del
argumentoI indicamos que parametros son de tipo referencia con el
operador & (pueden usarse como parametros de salida o deentrada/salida)
void A(int &i) {i = i-1;
}
void B(int i) {i = i-1;
}
void C() {int j = 6;
//vale j == 6;A(j);
//vale j == 5;B(j);
//vale j == 5;}
I no se puede pasar a A una expresion que no sea una variable(u otra descripcion de una posicion de memoria) 220
Pasaje por referencia para las variables en modifica
I en el lenguaje de especificacion sabemos indicar que los argumentosse modificanproblema swap(x , y : Int) {
modifica x , y ;
asegura x == pre(y) ∧ y == pre(x); }I los lenguajes imperativos permiten implementar esto directamente
void swap(int& x, int& y) {int z; //estado 1; vale x == pre(x) ∧ y == pre(y);z = x; //estado 2; vale z == x@1 ∧ x == x@1 ∧ y == y@1;x = y; //estado 3; vale z == z@2 ∧ x == y@2 ∧ y == y@2;y = z; //estado 4; vale z == z@3 ∧ x == x@3 ∧ y == z@3;
//implica x == pre(y) ∧ y == pre(x);}
I justificacion del implica: x == x@3 == y@2 == y@1 == pre(y);y == z@3 == z@2 == z@1 == pre(x)
I el estado 4 cumple la poscondicion (entonces, la funcion es correctarespecto de su especificacion)
221
¿Que hace esta funcion?void prueba(int &x, int &y) {
//estado 1; vale x == pre(x) ∧ y == pre(y);x = x + y;
//estado 2; vale y == y@1 ∧ x == x@1 + y@1;//implica x == pre(x) + pre(y); (pues y@1 == pre(y) y x@1 == pre(x))
y = x - y;//estado 3; vale x == pre(x) + pre(y) ∧ y == x@2− y@2;//implica y == pre(x) + pre(y)− pre(y); (pues y@2 == y@1 == pre(y))
//implica y == pre(x); (operaciones aritmeticas)
x = x - y;//estado 4; vale y == pre(x) ∧ x == x@3− y@3;//implica x == pre(x) + pre(y)− pre(x); (justificacion trivial)
//implica y == pre(x) ∧ x == pre(y); (operaciones aritmeticas)
}por lo tanto, esta funcion tambien es correcta respecto de la especificaciondel problema swap
I salvo que sea llamada con prueba(x,x)222
Invocacion de funciones - pasaje por valor
problema doble(x : Int) = res : Int {asegura res == 2 ∗ x ; }
problema cuad(x : Int) = res : Int {asegura res = 4 ∗ x ; }
int cuad(int x) {int c = doble(x);
//estado 1;//vale c == 2 ∗ x ; (usando la poscondicion de doble)
c = doble(c);//vale c == 2 ∗ c@1; (usando la poscondicion de doble)//implica c == 2 ∗ 2 ∗ x == 4 ∗ x ; (justificacion trivial)
return c;//vale res == 4 ∗ x ;
}
223
Invocacion de funciones - pasaje por referenciaproblema swap(x , y : Int) {
modifica x , y ;asegura x == pre(y) ∧ y == pre(x); }
problema swap3(a, b, c : Int) {modifica a, b, c ;asegura a == pre(b) ∧ b == pre(c) ∧ c == pre(a); }
void swap3(int& a, int& b, int& c) {//vale a == pre(a) ∧ b == pre(b) ∧ c == pre(c);
swap(a,b);//estado 1;//vale a == pre(b) ∧ b == pre(a); (usando la poscondicion de swap)//vale c == pre(c);
swap(b,c) ;//estado 2;//vale b == c@1 ∧ c == b@1; (usando la poscondicion de swap)//vale a == a@1;//implica a == pre(b) ∧ b == pre(c) ∧ c == pre(a); (justificar...)
}224
Programacion imperativaClase 2
Teorema del Invariante
225
AsignacionSemantica de la asignacion:
I sea e una expresion cuya evaluacion no modifica el estado
//estado av = e;
//vale v == e@a ∧ z1 = z1@a ∧ · · · ∧ zk = zk@a
I donde z1, . . . , zk son todas las variables del programa encuestion distintas a v que aparezcan en una clausula modificao local
I las otras variables se supone que no cambian ası que no hacefalta decir nada)
I si la expresion e es la invocacion a una funcion que recibeparametros por referencia, puede haber mas cambios, pero almenos
//vale v == e@a
esta en la poscondicion de la asignacion226
CondicionalesI supongamos este codigo:
if (B) uno else dos;
I B tiene que ser una expresion booleana (verdadera o falsa) sinefectos secundarios (no tiene que modificar el estado)
I se llama guardaI uno y dos son instrucciones
I en particular, pueden ser bloques (entre llaves)
I si sabemos que
//vale P ∧ Buno;
//vale Q
//vale P ∧ ¬Bdos;
//vale Q
I entonces podemos afirmar
//vale Pif (B) uno else dos;
//vale Q
I decimos que P es la precondicion del condicional y Q es suposcondicion 227
Condicionales
Equivalentemente:
I si sabemos que
//vale P ∧ Buno;
//vale Q1
//vale P ∧ ¬Bdos;
//vale Q2
I entonces podemos afirmar
//vale Pif (B) uno else dos;
//vale Q1 ∨ Q2
228
Ejemplo de demostracion de condicional
problema max(x , y : Int) = result : Int{asegura Q : (x > y ∧ result == x) ∨ (x ≤ y ∧ result == y)
}
int max(int x, int y) {int m = 0;
//vale Pif : m == 0;if (x > y)
m = x;else
m = y;//vale Qif : (x > y ∧m == x) ∨ (x ≤ y ∧m == y);
return m;//vale Q;
}
229
Ejemplo de demostracion de condicional
I demuestro cada rama fuera del condicionalI rama true:
//vale m == 0 ∧ x > y ;m = x;
//vale x > y ∧m == x ;//implica (x > y ∧m == x) ∨ (x ≤ y ∧m == y);(justificacion: p → (p ∨ q) es tautologıa)
I rama false://vale m == 0 ∧ x ≤ y ;
m = y;//vale x ≤ y ∧m == y ;//implica (x > y ∧m == x) ∨ (x ≤ y ∧m == y);(justificacion: p → (p ∨ q) es tautologıa)
I pudimos llegar a Qif por las dos ramas,I entonces demostre que el condicional es correcto para la
precondicion Pif y poscondicion Qif
230
Ciclos
I while (B) cuerpo;I B: expresion booleana, sin efectos colaterales
I tambien se la llama guarda
I cuerpo es una instruccionI en particular, puede ser un bloque (entre llaves)I se repite mientras B valga
I cero o mas vecesI si es una cantidad finita, el programa terminaI si es > 0, alguna de las ejecuciones tiene que hacer B falsaI y el estado final de esa ejecucion sera el estado final del ciclo
231
Ejemplo de ciclo
problema sumat(x : Int) = r : Int{requiere P : x ≥ 0asegura r ==
∑[0..x ]
}
I en funcionalsumat :: Int -> Intsumat 0 = 0
sumat n = n + (sumat (n-1))
I en imperativoint sumat (int x) {
int s = 0, i = 0;while (i < x) {
// estado 1i = i + 1;s = s + i;
// estado 2}return s;
}
estados para x == 4i@1 s@1 i@2 s@2
0 0 1 1
1 1 2 3
2 3 3 6
3 6 4 10
Observar que en cada paso:I 0 ≤ i ≤ x
I cuando i == x , sale del ciclo
I s ==∑
[0..i ]
Estas dos valen en estado 1 y estado2 (pero no en el medio) 232
Otro ejemplo de ciclo
problema fact(x : Int) = r : Int{requiere P : x ≥ 0asegura r ==
∏[1..x ]
}
I en funcionalfact :: Int -> Intfact 0 = 1
fact n = n * (fact (n-1))
I en imperativoint fact (int x) {
int f = 1, i = 0;while (i < x) {
// estado 1i = i + 1;f = f * i;
// estado 2}return f;
}
estados para x == 4i@1 f @1 i@2 f @2
0 1 1 1
1 1 2 2
2 2 3 6
3 6 4 24
Observar que en cada paso:I 0 ≤ i ≤ x
I cuando i == x , sale del ciclo
I f ==∏
[1..i ]
Estas dos valen en estado 1 y estado2 (pero no en el medio) 233
Semantica de ciclosI la semantica requiere cuatro expresiones del lenguaje de
especificacionI una precondicion PC
I una poscondicion QC
I un invariante II una guarda B
I invarianteI condicion cuya veracidad es preservada por el cuerpo del cicloI vale antes de entrar al ciclo (justo antes de evaluar la guarda por
primera vez)I vale en cada iteracion
I justo despues de entrar al cuerpo delciclo
I y justo despues de ejecutar la ultimainstruccion del cuerpo del ciclo
I pero pude no valer en el medio delcuerpo
//vale PC ;while (B) {
//vale I ∧ B;cuerpo//vale I ;
}//vale QC ;
I no hay forma algorıtmica de encontrarloI conviene darlo al escribir el ciclo, porque encierra la idea del
programador o disenador 234
Terminacion y correctitud
//vale PC ;while (B) {
//vale I ∧ B;cuerpo//vale I ;}
//vale QC ;
Herramientas:
I PC = precondicion del ciclo
I QC = poscondicion del ciclo
I I invariante
I B guarda
Especificacion del ciclo = (PC ,QC )
I un ciclo termina (con respecto a la especificacion del ciclo) sino importa para que valores de entrada que satisfagan PC , elciclo, despues de una cantidad finita de pasos termina (esdecir, se verifica ¬B)
I para demostrar esto, necesitamos herramientas adicionales:I funcion varianteI cota
I un ciclo es correcto (con respecto a la especificacion del ciclo)si termina y al salir satisface QC
I para demostrar esto, vamos a usar fuertemente el invariante
235
Terminacion
I ¿como podemos garantizar queel ciclo termina?
I similares a conceptos que vimospara programacion funcional
//vale PC ;while (B) {
//vale I ∧ B;cuerpo//vale I ;}
//vale QC ;I para hablar de la cantidad de veces que se ejecuta un ciclo
necesitamos:I expresion variante (v)
I es una expresion del lenguajede especificacion, de tipo Int
I usa variables del programa
I debe decrecer en cada paso
//estado a;//vale B ∧ I ;cuerpo//vale v < v@a;
I cota (c)I valor (fijo, por ejemplo 0 o −8) que, si es alcanzado o pasado
por la expresion variante, garantiza que la ejecucion sale delciclo
I mas formalmente: (I ∧ v ≤ c)→ ¬B236
Teorema de terminacion
TeoremaSi v es una expresion variante (con las propiedades de la p. 236) eI es un invariante (con las propiedades de la p. 234) de un ciclo yc es una cota tal que (I ∧ v ≤ c)→ ¬B, entonces el ciclo termina.
Demostracion.I por el absurdo: supongamos que el ciclo no termina
I sea vj el valor que toma v despues de ejecutar el cuerpo delciclo por j-esima vez (j ≥ 0)
I como v es estrictamente decreciente, v1 > v2 > v3 > . . .
I como v es de tipo Int, vj ∈ N para todo j
I debe existir un k tal que vk ≤ c
I como vale (I ∧ v ≤ c)→ ¬B, tenemos que para este k vale¬B y por lo tanto sale del ciclo (despues de iterar k veces)
I llegamos a un absurdo porque habıamos supuesto que el ciclono terminaba. Entonces el ciclo termina.
¿Que pasarıa si v fuese de tipo Float? ¿Funciona igual la demo? 237
Observaciones sobre terminacionint dec1(int x) {
while (x > 0)
x = x - 1;
return x;
}
int dec2(int x) {while (x != 0)
x = x - 1;return x;
}Supongamos que x no es negativo
I la implicacion (I ∧ v ≤ c)→ ¬B:I el invariante deberıa ser I : x ≥ 0 en los dos casosI ambos devuelven 0I iteran la misma cantidad de veces (x veces)
I ¿hace falta I en (I ∧ v ≤ c)→ ¬B?I en los dos casos, v = x y c = 0 deberıan ser buenas
x ≤ 0→ x 6> 0, luego no se usa I para dec1pero x ≤ 0 6→ x == 0, luego hay que usar I para dec2usando I en dec2 tenemos: (x ≥ 0 ∧ x ≤ 0)→ x == 0
I si c es una buena cota, cualquier cota k < c tambien:I −2 es buena cota para dec1: x ≤ −2→ x ≤ 0→ x 6> 0I −2 es buena cota para dec2: (x ≥ 0 ∧ x ≤ −2)→ cualquier cosa
I ¡no hay trampa en este ejemplo!I si v es una buena funcion variante con cota k, v ′ = v − k es una
buena funcion variante con cota 0I entonces sin perdida de generalidad, podemos suponer siempre una cota 0238
Teorema de Correctitud
TeoremaSi un ciclo que termina satisface PC → I y (I ∧ ¬B)→ QC
entonces el ciclo es correcto con respecto a su especificacion.
Demostracion.Queremos ver que el ciclo es correcto para su especificacion, esdecir, queremos ver que para variables que satisfagan PC , cuandoel ciclo termina se satisface QC
I supongamos que las variables en el estado 1 satisfacenPC
I como PC → I , entonces en el estado 1 se satisface I
I ejecutamos el ciclo (0 o mas veces)
I por definicion de invariante (ver p. 234), en cadaiteracion, el invariante se restablece
I por hipotesis, el ciclo termina y en el estado 2 vale ¬B
I ademas, en el estado 2 vale I
I como (I ∧ ¬B)→ QC entonces en el estado 2 vale QC
//estado 1//vale PC ;
while (B) {//vale I ∧ B;cuerpo//vale I ;}
//estado 2//vale QC ;
239
Todo junto: Teorema del Invariante
TeoremaSea while (B) { cuerpo } un ciclo con guarda B,precondicion PC y poscondicion QC . Sea I un predicado booleano,v una funcion variante y c una cota. Si valen
1. PC → I
2. (I ∧ ¬B)→ QC
3. el invariante se preserva en la ejecucion delcuerpo, i.e. si I ∧ B vale en el estado Aentonces I vale en el estado B
4. v es decreciente, i.e. v@A > v@B
5. (I ∧ v ≤ c)→ ¬B
//estado Acuerpo//estado B
entonces para cualquier valor de las variables del programa quehaga verdadera PC , el ciclo termina y hace verdadera QC , i.e. elciclo cumple con su especificacion (PC ,QC ).
Demostracion.Combinar el Teorema de Terminacion (p. 237) y el Teorema deCorrectitud (p. 239). 240
Ejemplo de demostracion de correctitudDemostracion de PC → I
problema sumat(x : Int) = r : Int{requiere P : x ≥ 0asegura r ==
∑[0..x ]
}
int sumat (int x) {int s = 0, i = 0;
//vale PC : s == 0 ∧ i == 0while (i < x) {
//invariante I : 0 ≤ i ≤ x ∧ s ==∑
[0..i ]//variante v : x − i
i = i + 1;s = s + i;
}//vale QC : i == x ∧ s ==
∑[0..x ]
return s;//vale r ==
∑[0..x ]
}
1. PC → I :
Supongamos que vale PC y veamosque vale cada parte de I
I 0 ≤ i : si i == 0 esto es trivialI i ≤ x : si i == 0 y x == 0
esto es trivialI s ==
∑[0..i ]: como s == 0 y
i == 0 hay que probar que0 ==
∑[0..0], pero esto es
trivial
241
Ejemplo de demostracion de correctitudDemostracion de (I ∧ ¬B)→ QC
problema sumat(x : Int) = r : Int{requiere P : x ≥ 0asegura r ==
∑[0..x ]
}
int sumat (int x) {int s = 0, i = 0;
//vale PC : s == 0 ∧ i == 0while (i < x) {
//invariante I : 0 ≤ i ≤ x ∧ s ==∑
[0..i ]//variante v : x − i
i = i + 1;s = s + i;
}//vale QC : i == x ∧ s ==
∑[0..x ]
return s;//vale r ==
∑[0..x ]
}
2. (I ∧ ¬B)→ QC :
Supongamos que vale I ∧ ¬B yveamos que vale cada parte de QC porseparado:
I i == x : de I sabemos quei ≤ x y de ¬B sabemos i ≥ x
I s ==∑
[0..x ]: ya vimos quei == x . De I sabemoss ==
∑[0..i ].
242
Ejemplo de demostracion de correctitudDemostracion de que el cuerpo preserva el invariante
problema sumat(x : Int) = r : Int{requiere P : x ≥ 0asegura r ==
∑[0..x ]
}
int sumat (int x) {int s = 0, i = 0;
//vale PC : s == 0 ∧ i == 0while (i < x) {
//invariante I : 0 ≤ i ≤ x ∧ s ==∑
[0..i ]//variante v : x − i
i = i + 1;s = s + i;
}//vale QC : i == x ∧ s ==
∑[0..x ]
return s;//vale r ==
∑[0..x ]
}
3. el cuerpo preserva el invariante:
Hacemos la transformacion de estadosdel cuerpo:
//estado 1
//vale B ∧ I
//implica 0 ≤ i < x ∧ s ==∑
[0..i ]
(justificacion trivial)
i = i + 1;
//estado 2
//vale i == 1 + i@1 ∧ s == s@1
s = s + i;
//estado 3
//vale i == i@2 ∧ s == s@2 + i@2243
Ejemplo de demostracion de correctitudDemostracion de que el cuerpo preserva el invariante (cont.)
Transformacion de estados del cuerpo
//estado 1//vale B ∧ I//implica 0 ≤ i < x ∧ s ==
∑[0..i ]
i = i + 1;//estado 2//vale i == 1 + i@1 ∧ s == s@1
s = s + i;//estado 3//vale i == i@2 ∧ s == s@2 + i@2
Queremos ver que vale I @3:
I i@3 ≥ 0: sabemosi@3 == i@2 == 1 + i@1 y por elestado 1 sabemos i@1 ≥ 0. Luego1 + i@1 ≥ 0
I i@3 ≤ x@3: ya vimos quei@3 == 1 + i@1. Como x no cambiaporque es una variable de entrada queno aparece ni en modifica ni en local,x@3 == x@1. Del estado 1 sabemosi@1 < x@1. Sumando 1 en amboslados, 1 + i@1 < 1 + x@1. De lo quedijimos antes, i@3 < 1 + x@3 y estoes equivalente a i@3 ≤ x@3.
I s@3 ==∑
[0..i@3]: ya vimos quei@3 == 1 + i@1. Sabemos ques@3 == s@2 + i@2 == 1 + s@1 + i@1.Como s@1 ==
∑[0..i@1], entonces
s@3 ==∑
[0..i@1] + 1 + i@1 ==∑[0..1 + i@1] ==
∑[0..i@3]. 244
Ejemplo de demostracion de correctitudDemostracion de que v es decreciente
problema sumat(x : Int) = r : Int{requiere P : x ≥ 0asegura r ==
∑[0..x ]
}
int sumat (int x) {int s = 0, i = 0;
//vale PC : s == 0 ∧ i == 0while (i < x) {
//invariante I : 0 ≤ i ≤ x ∧ s ==∑
[0..i ]//variante v : x − i
i = i + 1;s = s + i;
}//vale QC : i == x ∧ s ==
∑[0..x ]
return s;//vale r ==
∑[0..x ]
}
4. v es decreciente:
Recordemos la transformacion deestados
//estado 1
//vale B ∧ I
//implica 0 ≤ i < x ∧ s ==∑
[0..i ]
i = i + 1;
//estado 2
//vale i == 1 + i@1 ∧ s == s@1
s = s + i;
//estado 3
//vale i == i@2 ∧ s == s@2 + i@2
I v@3 == x@3− i@3 == pre(x)− i@3I v@1 == x@1− i@1 == pre(x)− i@1I ya vimos que i@3 = 1 + i@1I luego v@3 == pre(x)− (1 + i@1) <
pre(x)− i@1 == v@1245
Ejemplo de demostracion de correctitudDemostracion de (I ∧ v ≤ c)→ ¬B
problema sumat(x : Int) = r : Int{requiere P : x ≥ 0asegura r ==
∑[0..x ]
}
int sumat (int x) {int s = 0, i = 0;
//vale PC : s == 0 ∧ i == 0while (i < x) {
//invariante I : 0 ≤ i ≤ x ∧ s ==∑
[0..i ]//variante v : x − i
i = i + 1;s = s + i;
}//vale QC : i == x ∧ s ==
∑[0..x ]
return s;//vale r ==
∑[0..x ]
}
5. (I ∧ v ≤ c)→ ¬B:
Supongamos que vale I ∧ v ≤ c
I v ≤ c es equivalente ax − i ≤ 0, que es equivalentea x ≤ i y esto es ¬B
246
Programacion imperativaClase 3
Algoritmos de busqueda
247
Arreglos
I secuencias de una cantidad fija de variables del mismo tipoI se declaran con un nombre, un tamano y un tipo
I int a[10];I arreglo de 10 elementos enterosI nos referimos a los elementos a traves del nombre del arreglo y
un ındice entre corchetesI a[0], a[1],..., a[9]
I solamente hay valores en las posiciones validasI de 0 a la dimension menos unoI una referencia a una posicion fuera del rango da error (en
ejecucion)I el tamano se mantiene constante
248
Arreglos y listas
I ambos representan secuencias de elementos de un tipo
I los arreglos tienen longitud fija; las listas, noI los elementos de un arreglo pueden accederse en forma
independienteI los de la lista se acceden secuencialmente, empezando por la
cabezaI para acceder al i-esimo elemento de una lista, hay que obtener
i veces la cola y luego la cabezaI para acceder al i-esimo elemento de un arreglo, simplemente se
usa el ındice
249
Arreglos en C++
I los arreglos en C++ son referenciasI pueden modificarse cuando son pasados como argumentos
I no hay forma de averiguar su tamano una vez que fueron creadosI el programa tiene que encargarse de almacenarlo de alguna formaI en la declaracion de un parametro no se indica su tamano
problema ceroPorUno(a : [Int], tam : Int){requiere tam == |a|;modifica a;
asegura a == [if i == 0 then 1 else i | i ← pre(a)[0..tam)]; }
void ceroPorUno(int a[], int tam) {int j = 0;while (j < tam) {
// invariante 0 ≤ j ≤ tam ∧ a[j ..tam) == pre(a)[j ..tam)∧// a[0..j) == [if i == 0 then 1 else i | i ← pre(a)[0..j)];// variante tam − j ;if (a[j] == 0) a[j] = 1;j++;
}}
250
Busqueda lineal sobre arreglosI Especificacion
problema buscar (a : [Int], x : Int, n : Int) = res : Bool{requiere n == |a| ∧ n > 0;asegura res == (x ∈ a);
}I Implementacion
bool buscar (int a[], int x, int n) {int i = 0;while (i < n && a[i] != x) {
i = i + 1;}return i < n;
}
El algoritmo de busqueda lineal
I es el mas ingenuoI revisa los elementos del arreglo en ordenI en el peor caso realiza n operaciones
251
Especificacion del ciclo
bool buscar(int a[], int x, int n) {int i = 0;
// vale Pc : i == 0
while (i < n && a[i] != x) {
// invariante I : 0 ≤ i ≤ n ∧ x /∈ a[0..i)// variante v : n − i
i = i + 1;}
// vale Qc : i < n↔ x ∈ a[0..n)
return i < n;}
252
Correctitud del ciclo
// vale Pc// implica I
while (i < n && a[i] != x) {
// estado E// vale I ∧ i < n ∧ a[i ] 6= x
i = i+1;
// estado F// vale I// vale v < v@E
}
// vale I ∧ ¬(i < n ∧ a[i ] 6= x)// implica Qc
1. El cuerpo del ciclo preservael invariante
2. La funcion variante decrece
3. Si la funcion variante pasa lacota, el ciclo termina:v ≤ 0⇒ ¬(i < n ∧ a[i ] 6= x)
4. La precondicion del cicloimplica el invariante
5. La poscondicion vale al final
El Teorema del Invariante nos garantiza que si valen 1, 2, 3, 4 y 5,el ciclo termina y es correcto con respecto a su especificacion.
253
1. El cuerpo del ciclo preserva el invarianteRecordar que
I el invariante es I : 0 ≤ i ≤ n ∧ x /∈ a[0..i)
I la guarda B es i < n ∧ a[i ] 6= x
// estado E (invariante + guarda del ciclo)// vale 0 ≤ i ≤ n ∧ x /∈ a[0..i) ∧ i < n ∧ a[i ] 6= x// implica 0 ≤ i < n (juntando 0 ≤ i ≤ n y i < n)// implica x /∈ a[0..i ] (juntando x /∈ a[0..i) y a[i ] 6= x)// implica x /∈ a[0..i + 1) (propiedad de listas)
i = i + 1;
// estado F// vale i = i@E + 1// implica 1 ≤ i@E + 1 < n + 1 (esta en E y sumando 1 en cada termino)// implica 0 ≤ i ≤ n (usando que i == i@E + 1 y propiedad de Z)// implica x /∈ a[0..i@E + 1) (esta en E ; a no puede cambiar)// implica x /∈ a[0..i) (usando que i == i@E + 1)// implica I (se reestablece el invariante)
254
2. La funcion variante decrece
// estado E (invariante + guarda del ciclo)// vale I ∧ B
i = i + 1;
// estado F// vale i == i@E + 1
¿Cuanto vale v = n − i en el estado F ?
v@F == (n − i)@F
== n − i@F
== n − (i@E + 1)
== n − i@E − 1
< n − i@E
== v@E
255
5. La poscondicion vale al final
Quiero probar que:
1︷ ︸︸ ︷0 ≤ i ≤ n ∧
2︷ ︸︸ ︷x /∈ a[0..i) ∧
5︷ ︸︸ ︷(i ≥ n ∨ x == a[i ])
⇓Qc : i < n︸ ︷︷ ︸
3
↔ x ∈ a[0..n)︸ ︷︷ ︸4
Demostracion:
I supongamos i ≥ n (i.e. 3 es falso). Por 1, i == n. Por 2,tenemos x /∈ a[0..n). Luego 4 tambien es falso.
I supongamos i < n (i.e. 3 es verdadero). Por 5, x == a[i ]. De1 concluimos que 4 es verdadero.
256
3 y 4 son triviales
3. Si la funcion variante pasa la cota, el ciclo termina:
n − i ≤ 0 ⇒ n ≤ i ⇒ ¬B
4. La precondicion del ciclo implica el invariante
i == 0 ⇒ 0 ≤ i ≤ n ∧ x /∈ a[0..i)
Entonces el ciclo es correcto con respecto a su especificacion.Observar que
// estado H// vale Qc : i < n↔ x ∈ a[0..n)
return i < n;
// vale res == (i < n)@H ∧ i = i@H// implica res == x ∈ a[0..n) (esta es la poscondicion del problema)
257
Complejidad de un algoritmo
I maxima cantidad de operaciones que puede realizar elalgoritmo
I hay que buscar los datos de entrada que lo hacen trabajar mas
I se define como funcion del tamano de la entradaI T (n), donde n es la cantidad de datos recibidos
I notacion O grandeI decimos que T (n) es O(f (n)) cuando
I T (n) es a lo sumo una constante por f (n)I excepto para valores pequenos de n
I T (n) es O(f (n)) sii (∃c , n0)(∀n ≥ n0) T (n) ≤ c · f (n)I se suele leer T (n) es del orden de f (n)
258
Complejidad de la busqueda lineal
I ¿cuando se da la maxima cantidad de pasos?I cuando el elemento no esta en la estructura
I ¿cuantos pasos son?I tantos como elementos tenga la estructura
I la complejidad del algoritmo es del orden del tamano de laestructura
I en un arreglo o lista, hay que hacer tantas comparaciones (ysumas) como elementos tenga el arreglo o lista
I si llamamos n a la longitud de la estructuraI el algoritmo tiene complejidad O(n)I en cada paso tengo cuatro operaciones (dos comparaciones,
una conjuncion y el incremento del ındice), entonces c = 4I pero esto no es importanteI lo importante es como crece la complejidad cuando crece la
estructura
259
Programacion imperativaClase 4
Algoritmos de busqueda
260
Mejorando la busqueda
Supongamos que tenemos un diccionario y queremos buscar unapalabra x .
¿Como podemos mejorar la busqueda?
I abrimos el diccionario a la mitad
I si x esta en esa pagina, terminamos
I si x es menor (segun el orden de diccionario) que las palabrasde la pagina actual, x no puede estar en la parte derecha
I si x es mayor (segun el orden de diccionario) que las palabrasde la pagina actual, x no puede estar en la parte izquierda
I seguimos buscando solo en la parte izquierda o derecha (segunsea el caso)
261
Busqueda binaria - especificacion del problemaSupongamos que en lugar de un diccionario tenemos un arregloordenado y en lugar de buscar palabras buscamos numeros enteros.
Podemos hacer una busqueda mas eficiente
I revisamos solo algunas posiciones del arreglo: en cada pasodescartamos la mitad del espacio de busqueda
I menos comparaciones
Este tipo de algoritmo se llama busqueda binaria.
Tenemos un nuevo problema en donde
I mantenemos la poscondicionI reforzamos la precondicion
problema buscarBin (a : [Int], x : Int, n : Int) = res : Bool{requiere |a| == n > 0;requiere (∀j ∈ [0..n − 1)) a[j ] ≤ a[j + 1];asegura res == (x ∈ a);
}262
El programa
bool buscarBin(int a[], int x, int n) {int i = 0, d = n - 1;bool res; int m;if (x < a[i] || x > a[d]) res = false;else {
while (d > i + 1) {m = (i + d) / 2;if (x == a[m]) i = d = m;else if (x < a[m]) d = m;else i = m;
}res = (a[i] == x || a[d] == x);
}return res;
}
Ejemplo n = 12, x = 14, a = 2 5 5 6 8 12 14 19 22 27 27 290 1 2 3 4 5 6 7 8 9 10 11
Ni
Nd
NmNi
Nd
Nm
Ni
Nd
NmN
i=m=dN
i=m=d
Ejemplo n = 12, x = 13, a = 2 5 5 6 8 12 14 19 22 27 27 290 1 2 3 4 5 6 7 8 9 10 11
Ni
Nd
NmNi
Nd
Nm
Ni
Nd
Nm
NiNd
263
Especificacion del ciclo
bool buscarBin(int a[], int x, int n) {int i = 0, d = n - 1; bool res; int m;if (x < a[i] || x > a[d]) res = false;else {
// vale Pc : i == 0 ∧ d == n − 1 ∧ a[i ] ≤ x ≤ a[d ]
while (d > i + 1) {
// invariante I : 0 ≤ i ≤ d < n ∧ a[i ] ≤ x ≤ a[d ]// variante v : d − i − 1
m = (i + d) / 2;if (x == a[m]) i = d = m;else if (x < a[m]) d = m;else i = m;
}
// vale Qc : 0 ≤ i < n ∧ 0 ≤ d < n ∧ x ∈ a↔ (a[i ] == x ∨ a[d ] == x)
res = (a[i] == x || a[d] == x);}return res;
}
por la inicializacion
por el if
i y d son ındicesvalidos del arreglo
i siempre estaa la izquierda de d(o son iguales, peronunca se cruzan)
i y d delimitan elespacio de busqueda(se hereda de Pc)
es, basicamente, lalongitud del espaciode busqueda (resto1 para garantizar quev ≤ 0⇒ d ≤ i + 1)
264
Correctitud del ciclo
// vale Pc// implica I
while (d > i + 1) {
// estado E// vale I ∧ d > i + 1
m = (i + d) / 2;if (x == a[m]) i = d = m;else if (x < a[m]) d = m;else i = m;
// vale I// vale v < v@E
}
// vale 0 ≤ i < n ∧ 0 ≤ d < n ∧x ∈ a↔ (a[i ] == x ∨ a[d ] == x)// implica Qc
1. El cuerpo del ciclopreserva el invariante
2. La funcion variantedecrece
3. Si la funcion variante pasala cota, el ciclo termina:v ≤ 0⇒ d ≤ i + 1
4. La precondicion del cicloimplica el invariante
5. La poscondicion vale alfinal
El Teorema del Invariante nos garantiza que si valen 1, 2, 3, 4 y 5,el ciclo termina y es correcto con respecto a su especificacion.
265
1. El cuerpo del ciclo preserva el invariante// estado E (invariante + guarda del ciclo)// vale 0 ≤ i ∧ i + 1 < d < n ∧ a[i ] ≤ x ≤ a[d ]
m = (i + d) / 2;// estado F// vale m = (i + d)@E div 2 ∧ i = i@E ∧ d = d@E// implica 0 ≤ i < m < d < n
if (x == a[m]) i = d = m;else if (x < a[m]) d = m;else i = m;
// vale m == m@F// vale (x == a[m@F ] ∧ i == d == m@F ) ∨
(x < a[m@F ] ∧ d == m@F ∧ i == i@F ) ∨(x > a[m@F ] ∧ i == m@F ∧ d == d@F )
Falta ver que esto ultimo implica el invariante
I 0 ≤ i ≤ d < n: sale del estado FI a[i ] ≤ x ≤ a[d ]:
I caso x == a[m]: es trivial pues a[i ] == a[m] == a[d ] == xI caso x < a[m]: tenemos a[i ] ≤ x < a[m] == a[d ]I caso x > a[m]: tenemos a[i ] == a[m] < x ≤ a[d ]
266
2. La funcion variante decrece// estado E (invariante + guarda del ciclo)// vale 0 ≤ i ∧ i + 1 < d < n ∧ a[i ] ≤ x ≤ a[d ]// implica d − i − 1 > 0
m = (i + d) / 2;// estado F// vale m == (i + d) div 2 ∧ i == i@E ∧ d == d@E// implica 0 ≤ i < m < d < n
if (x == a[m]) i = d = m;else if (x < a[m]) d = m;else i = m;
// vale m == m@F// vale (x == a[m@F ] ∧ i == d == m@F ) ∨
(x < a[m@F ] ∧ d == m@F ∧ i == i@F ) ∨(x > a[m@F ] ∧ i == m@F ∧ d == d@F )
¿Cuanto vale v = d − i − 1?
I caso x == a[m]: es trivial pues v = −1I caso x < a[m]: d decrece pero i queda igualI caso x > a[m]: i crece pero d queda igual
267
3 y 4 son triviales
3. Si la funcion variante pasa la cota, el ciclo termina:
d − i − 1 ≤ 0 ⇒ d ≤ i + 1
4. La precondicion del ciclo implica el invariante
Pc : i == 0 ∧ d == n − 1 ∧ a[i ] ≤ x ≤ a[d ]
⇓I : 0 ≤ i ≤ d < n ∧ a[i ] ≤ x ≤ a[d ]
268
5. La poscondicion vale al finalQuiero probar que:
1︷ ︸︸ ︷0 ≤ i ≤ d < n ∧
2︷ ︸︸ ︷a[i ] ≤ x ≤ a[d ] ∧
3︷ ︸︸ ︷d ≤ i + 1
⇓0 ≤ i < n ∧ 0 ≤ d < n︸ ︷︷ ︸
4
∧ x ∈ a︸ ︷︷ ︸5
↔ (a[i ] == x ∨ a[d ] == x)︸ ︷︷ ︸6
Demostracion:
I Por 1, resulta 4 verdadero.I Supongamos 6 verdadero. De 1 concluimos que 5 es
verdadero.I Supongamos a[i ] 6= x ∧ a[d ] 6= x (i.e. 6 es falso): a[j ] == x
para algun jI i < j < d : contradice 3, d ≤ i + 1I j < i : gracias al orden, x == a[j ] < a[i ]; contradice 2, x ≥ a[i ]I j > d : gracias al orden, x == a[j ] > a[d ]; contradice 2,
x ≤ a[d ]
Entonces el ciclo es correcto con respecto a su especificacion.269
Complejidad
¿Cuantas comparaciones hacemos como maximo?
I en cada iteracion mequedo con la mitad delespacio de busqueda (talvez uno mas; no influyeen el orden)
I paramos cuando elsegmento de busquedatiene longitud 1 o 2
numero deiteracion
longitud delespacio de busqueda
1 n2 n/23 (n/2)/2 = n/22
4 (n/22)/2 = n/23
......
t n/2t−1
En total hacemos t iteraciones
1 = n/2t−1 ⇒ 2t−1 = n ⇒ t = 1 + log2 n.
Luego, la complejidad de la busqueda binaria es O(log2 n). Muchomejor que la busqueda lineal, que es O(n).
270
Conclusiones
I vimos dos algoritmos de busqueda
I en general, mas informacion → algoritmos mas eficientes
problema buscar (a : [Int], x : Int, n : Int) = res : Boolrequiere |a| == n > 0;asegura res == (x ∈ a);
problema buscarBin (a : [Int], x : Int, n : Int) = res : Boolrequiere |a| == n > 0;requiere (∀j ∈ [0..n − 1)) a[j ] ≤ a[j + 1];asegura res == (x ∈ a);
I la busqueda binaria es esencialmente mejor que la busquedalineal - O(log2 n) vs. O(n)
271
Programacion imperativaClase 5
Algoritmos de ordenamiento
272
Ordenamiento de un arreglo
I tenemos un arreglo de un tipo T con una relacion de orden(≤)
I queremos modificar el arregloI para que sus elementos queden en orden crecienteI vamos a hacerlo permutando elementos
I nuestra poscondicion se va a parecer a la precondicion de labusqueda binaria
273
La especificacion
problema sort<T> (a : [T], n : Int){requiere 1 ≤ n == |a|;modifica a;asegura mismos(a, pre(a)) ∧ (∀j ← [0..n − 1)) aj ≤ aj+1;
}
aux cuenta(x : T, a : [T]) : Int = |[y | y ← a, y == x ]|;
aux mismos(a, b : [T]) : Bool = |a| == |b| ∧(∀x ← a) cuenta(x , a) == cuenta(x , b);
T tiene que ser un tipo con una relacion de orden ≤ definida
274
El algoritmo Upsort
I ordenamos de derecha a izquierda
I el segmento desordenado va desde el principio hasta laposicion que vamos a llamar actual
I comenzamos con actual = n − 1I mientras actual > 0
I encontrar el mayor elemento del segmento todavıa no ordenadoI intercambiarlo con el de la posicion actualI decrementar actual
Ejemplo 9 5 2 7 1 20 1 2 3 4 5
Nactual
Nm
Ejemplo 2 5 2 7 1 90 1 2 3 4 5
NactualNm
Ejemplo 2 5 2 1 7 90 1 2 3 4 5
Nactual
Nm
Ejemplo 2 1 2 5 7 90 1 2 3 4 5
NactualNm
Ejemplo 2 1 2 5 7 90 1 2 3 4 5
NactualNm
Ejemplo 1 2 2 5 7 90 1 2 3 4 5
Nactual
275
El programa
void upsort (int a[], int n) {int m, actual = n-1;while (actual > 0) {
m = maxPos(a,0,actual);swap(a[m],a[actual]);actual--;
}}
problema maxPos(a : [Int], desde, hasta : Int) = pos : Int{requiere 0 ≤ desde ≤ hasta ≤ |a| − 1;asegura desde ≤ pos ≤ hasta ∧ (∀x ← a[desde..hasta]) x ≤ apos ;
}
problema swap(x , y : Int){modifica x , y ;asegura x == pre(y) ∧ y == pre(x);
}276
Correctitud de Upsort
void upsort (int a[], int n) {
// vale P : 1 ≤ n == |a| (es la precondicion del problema)
int m, actual = n-1;
// vale PC : a == pre(a) ∧ actual == n − 1 (es la precondicion del ciclo)
while (actual > 0) {m = maxPos(a,0,actual);swap(a[m],a[actual]);actual--;
}
// vale QC : mismos(a, pre(a)) ∧ (∀j ← [0..n − 1)) aj ≤ aj+1
(es la poscondicion del ciclo)// vale Q : mismos(a, pre(a)) ∧ (∀j ← [0..n − 1)) aj ≤ aj+1
(es la poscondicion del problema)
}
Como QC ⇒ Q, lo que queda es probar que el ciclo es correcto para su
especificacion.277
Especificacion del ciclo
void upsort (int a[], int n) {int m, actual = n-1;
// vale PC : a == pre(a) ∧ actual == n − 1
while (actual > 0) {
// invariante I : 0 ≤ actual ≤ n − 1 ∧mismos(a, pre(a))∧ (∀k ← (actual ..n − 1)) ak ≤ ak+1
∧ actual < n − 1→ (∀x ← a[0..actual ]) x ≤ aactual+1
// variante v : actual
m = maxPos(a,0,actual);swap(a[m],a[actual]);actual--;
}
// vale QC : mismos(a, pre(a)) ∧ (∀j ← [0..n − 1)) aj ≤ aj+1
}
en cada paso actualse decrementa.
Comienza en n − 1y termina en 0
en cada paso el arreglo
es permutacion
del original
en cada paso
el segmento desde
actual + 1 hasta n − 1
esta ordenado
en cada paso actual + 1
tiene el maximo valordel segmento desde 0
hasta actual + 1
278
Correctitud del ciclo
// vale PC
// implica I
while (actual > 0) {
// estado E// vale I ∧ actual > 0
m = maxPos(a,0,actual);swap(a[m],a[actual]);actual--;
// vale I// vale v < v@E
}
// vale I ∧ ¬(actual > 0)// implica QC
1. El cuerpo del ciclo preservael invariante
2. La funcion variante decrece
3. Si la funcion variante pasa lacota, el ciclo termina:v ≤ 0⇒ ¬(actual > 0)
4. La precondicion del cicloimplica el invariante
5. La poscondicion vale al final
El Teorema del Invariante nos garantiza que si valen 1, 2, 3, 4 y 5,el ciclo termina y es correcto con respecto a su especificacion.
279
1. El cuerpo del ciclo preserva el invariante// estado E (invariante + guarda del ciclo)// vale 0 < actual ≤ n − 1 ∧mismos(a, pre(a))
∧(∀k ← (actual ..n − 1)) ak ≤ ak+1
∧(actual < n − 1→ (∀x ← a[0..actual ]) x ≤ aactual+1)
m = maxPos(a,0,actual);
// estado E1 Recordar especificacion de MaxPos:PMP : 0 ≤ desde ≤ hasta ≤ |a| − 1, se cumple porque 0 < atual ≤ n − 1QMP : desde ≤ pos ≤ hasta ∧ (∀x ← a[desde..hasta]) x ≤ apos
// vale 0 ≤ m ≤ actual ∧ (∀x ← a[0..actual ]) x ≤ am
// vale a == a@E ∧ actual == actual@E// implica 0 < actual ≤ n − 1 ∧mismos(a, pre(a))// implica (∀k ← (actual ..n − 1)) ak ≤ ak+1
// implica (actual < n − 1→ (∀x ← a[0..actual ]) x ≤ aactual+1)(Justificacion de los implica: actual y a no cambiaron–lo dice el segundo vale de este estado
)280
1. El cuerpo del ciclo preserva el invariante (cont.)// estado E1
// vale 0 ≤ m ≤ actual ∧ (∀x ← a[0..actual ]) x ≤ am
// implica 0 < actual ≤ n − 1 ∧mismos(a, pre(a))// implica (∀k ← (actual ..n − 1)) ak ≤ ak+1
// implica actual < n − 1→ (∀x ← a[0..actual ]) x ≤ aactual+1
swap(a[m],a[actual]);
// estado E2
// vale am == (a@E1)actual ∧ aactual == (a@E1)m (por poscon. de swap)// vale (∀i ← [0..n), i 6= m, i 6= actual) ai == (a@E1)i (idem)// vale actual == actual@E1 ∧m == m@E1
// implica 0 < actual ≤ n − 1 (actual no se modifico)// implica mismos(a, pre(a)) (el swap no agrega ni quita elementos)// implica (∀k ← (actual ..n − 1)) ak ≤ ak+1
(a(actual ..n − 1] no se modifico porque m ≤ actual)// implica (∀k ← (actual − 1..n − 1)) ak ≤ ak+1 del tercer vale de E2: m == m@E1 y actual == actual@E1;
del primer vale de y ultimo implica de E1: (a@E1)m ≤ (a@E1)actual+1;del primer y segundo vale de E2: aactual ≤ aactual+1)
// implica (∀x ← a[0..actual ]) x ≤ aactual (del primer vale de E1 y E2) 281
1. El cuerpo del ciclo preserva el invariante (cont.)// estado E2
// implica 0 < actual ≤ n − 1// implica mismos(a, pre(a))// implica (∀k ← (actual − 1..n − 1)) ak ≤ ak+1
// implica (∀x ← a[0..actual ]) x ≤ aactual
actual--;
// estado E3
// vale actual == actual@E2 − 1 ∧ a == a@E2
// implica 0 ≤ actual@E2 − 1 < n − 1 (de primer vale de E3)// implica 0 ≤ actual ≤ n − 1 (reemplazando actual@E2 − 1 por actual)// implica mismos(a, pre(a)) (de segundo implica de E2 y a == a@E2)// implica (∀k ← (actual@E2 − 1..n − 1)) ak ≤ ak+1 (por E2)// implica (∀k ← (actual ..n − 1)) ak ≤ ak+1
(reemplazo actual@E2 − 1 por actual)// implica (∀x ← a[0..actual + 1]) x ≤ aactual+1 (por E2 + reemplazo)// implica (∀x ← a[0..actual ]) x ≤ aactual+1
(por ser un selector mas acotado)// implica actual < n − 1→ (∀x ← a[0..actual ]) x ≤ aactual+1
(pues q implica p → q)282
2 y 3 son triviales
2. La funcion variante decrece:
// estado E (invariante + guarda del ciclo)// vale I ∧ B
m = maxPos(a,0,actual);swap(a[m],a[actual]);actual--;
// estado F// vale actual == actual@E − 1// implica v@F == v@E − 1 < v@E
3. Si la funcion variante pasa la cota, el ciclo termina:
actual ≤ 0 es ¬B
283
4. La precondicion del ciclo implica el invarianteRecordar que
I P : 1 ≤ n == |a|I PC : a == pre(a) ∧ actual == n − 1I I : 0 ≤ actual ≤ n − 1 ∧mismos(a, pre(a))
∧(∀k ← (actual ..n − 1)) ak ≤ ak+1
∧actual < n−1→ (∀x ← a[0..actual ]) x ≤ aactual+1
Demostracion de que PC ⇒ I :
I 1 ≤ n ∧ actual == n − 1 ⇒ 0 ≤ actual ≤ n − 1
I a == pre(a) ⇒ mismos(a, pre(a))
I actual == n − 1 ⇒ (∀k ← (actual ..n − 1)) ak ≤ ak+1
porque el selector actua sobre una lista vacıa
I actual == n − 1 ⇒actual < n − 1→ (∀x ← a[0..actual ]) x ≤ aactual+1
porque el antecedente es falso
284
5. La poscondicion vale al finalQuiero probar que: (¬B ∧ I )⇒ QC
Recordemos
¬B ∧ I : 0 ≤ actual ≤ n − 1 ∧mismos(a, pre(a)) ∧ (∀k ← (actual ..n − 1)) ak ≤ ak+1 ∧actual < n − 1→ (∀x ← a[0..actual + 1]) x ≤ aactual+1 ∧actual ≤ 0
QC : mismos(a, pre(a)) ∧ (∀j ← [0..n − 1)) aj ≤ aj+1
Veamos que vale cada parte de QC :
I mismos(a, pre(a)): trivial porque esta en II (∀j ← [0..n − 1)) aj ≤ aj+1:
I primero observar que actual == 0I si n == 1, no hay nada que probar porque [0..n − 1) == []I si n > 1
I sabemos (∀k ← (0..n − 1)) ak ≤ ak+1
I sabemos que (∀x ← a[0..1]) x ≤ a1, entonces a0 ≤ a1
I concluimos (∀j ← [0..n − 1)) aj ≤ aj+1
285
Implementacion de maxPos
problema maxPos (a : [Int], desde, hasta : Int) = pos : Int{requiere PMP : 0 ≤ desde ≤ hasta ≤ |a| − 1;asegura QMP : desde ≤ pos ≤ hasta ∧
(∀x ← a[desde..hasta]) x ≤ apos ;}
int maxPos(const int a[], int desde, int hasta) {int mp = desde, i = desde;while (i < hasta) {
i++;if (a[i] > a[mp]) mp = i;
}return mp;
}
286
Correctitud de maxPosproblema maxPos (a : [Int], desde, hasta : Int) = pos : Int{
requiere PMP : 0 ≤ desde ≤ hasta ≤ |a| − 1;asegura QMP : desde ≤ pos ≤ hasta ∧
(∀x ← a[desde..hasta]) x ≤ apos ;}
int maxPos(const int a[], int desde, int hasta) {
//vale PMP : 0 ≤ desde ≤ hasta ≤ |a| − 1 (precondicion del problema)
int mp = desde, i = desde;
//vale PC : mp == i == desde (precondicion del ciclo)
while (i < hasta) {i++;if (a[i] > a[mp]) mp = i;
}
//vale QC : desde ≤ mp ≤ hasta ∧ (∀x ← a[desde..hasta]) x ≤ amp
(poscondicion del ciclo)return mp;
//vale QMP : desde ≤ pos ≤ hasta ∧ (∀x ← a[desde..hasta]) x ≤ apos
(poscondicion del problema)} 287
Especificacion del ciclo
// vale PC : mp == i == desde
while (i < hasta) {
// invariante I : desde ≤ mp ≤ i ≤ hasta ∧ (∀x ← a[desde..i ]) x ≤ amp
// variante v : hasta− i
i++;if (a[i] > a[mp]) mp = i;
}
// vale QC : desde ≤ mp ≤ hasta ∧ (∀x ← a[desde..hasta]) x ≤ amp
288
Correctitud del ciclo
// vale PC
// implica I
while (i < hasta) {
// estado E// vale I ∧ i < hasta
i++;if (a[i] > a[mp]) mp = i;
// vale I// vale v < v@E
}
// vale I ∧ i ≥ hasta// implica QC
1. El cuerpo del ciclopreserva el invariante
2. La funcion variantedecrece
3. Si la funcion variante pasala cota, el ciclo termina:v ≤ 0⇒ i ≥ hasta
4. La precondicion del cicloimplica el invariante
5. La poscondicion vale alfinal
El Teorema del Invariante nos garantiza que si valen 1, 2, 3, 4 y 5,el ciclo termina y es correcto con respecto a su especificacion.
289
1. El cuerpo del ciclo preserva el invarianteRecordar que desde, hasta y a no cambian porque son variables deentrada que no aparecen en local ni modifica// estado E (invariante + guarda del ciclo)// vale desde ≤ mp ≤ i < hasta ∧ (∀x ← a[desde..i ]) x ≤ amp
i++;// estado E1
// vale i == i@E + 1 ∧mp = mp@E// implica Pif : desde ≤ mp ≤ i ≤ hasta ∧ (∀x ← a[desde..i)) x ≤ amp(
de E , reemplazando i@E por i − 1y cambiando el lımite del selector adecuadamente
)if (a[i] > a[mp]) mp = i;
// vale Qif : desde ≤ mp ≤ i ≤ hasta ∧ (∀x ← a[desde..i ]) x ≤ amp observar que en este punto, tratamos al if como una solagran instruccion que convierte Pif en Qif . La justificacionde este paso es la transformacion de estados de la hoja siguiente
// implica I(
observar que I es igual que Qif , pero en generalalcanzarıa con que Qif implique I
)290
Especificacion y correctitud del If
Pif : desde ≤ mp ≤ i ≤ hasta ∧ (∀x ← a[desde..i)) x ≤ amp
Qif : desde ≤ mp ≤ i ≤ hasta ∧ (∀x ← a[desde..i ]) x ≤ amp
// estado Eif
// vale desde ≤ mp ≤ i ≤ hasta ∧ (∀x ← a[desde..i)) x ≤ amp
if (a[i] > a[mp]) mp = i;
// vale (ai > amp@Eif∧mp == i@Eif ∧ i == i@Eif )
∨(ai ≤ amp@Eif∧mp == mp@Eif ∧ i == i@Eif )
// implica desde ≤ mp ≤ i ≤ hastaoperaciones logicas; observar que desde, hasta y ano pueden modificarse porque son variables de entrada queno aparecen ni en modifica ni en local ;observar que i == i@Eif
// implica amp@Eif
≤ amp ∧ ai ≤ amp (Justificar...)// implica (∀x ← a[desde..i ]) x ≤ amp
// implica Qif
291
2. La funcion variante decrece// estado E (invariante + guarda del ciclo)// vale desde ≤ i < hasta
i++;
// estado E1
// vale i == i@E + 1// implica desde ≤ i ≤ hasta
if (a[i] > a[mp]) mp = i;
// estado F// vale i == i@E1
// implica i == i@E + 1
¿Cuanto vale v = hasta− i en el estado F ?
v@F == (hasta− i)@F
== hasta− i@F
== hasta− (i@E + 1)
== hasta− i@E − 1
< hasta− i@E
== v@E 292
3, 4 y 5 son triviales
3. Si la funcion variante pasa la cota, el ciclo termina:
hasta− i ≤ 0 ⇒ hasta ≤ i
4. La precondicion del ciclo implica el invariante:Recordar que la precondicion de maxPos dice
PMP : 0 ≤ desde ≤ hasta ≤ |a| − 1
y que desde y hasta no cambian de valor. Entonces
PC : i == desde ∧mp == desde
⇓I : desde ≤ mp ≤ i ≤ hasta ∧ (∀x ← a[desde..i ]) x ≤ amp
5. La poscondicion vale al final: es facil ver que I ∧ i ≥ hastaimplica QC
Entonces el ciclo es correcto con respecto a su especificacion.293
Complejidad de maxPos
¿Cuantas comparaciones hacemos como maximo?
I el ciclo itera hasta− desde vecesI cada iteracion hace
I una comparacion (en la guarda)I una asignacion (el incremento)I otra comparacion (guarda del condicional)I otra asignacion (si se cumple esa guarda)
I antes del ciclo se hacen dos asignaciones
I todo estos detalles no importan para calcular el orden
I O(longitud del segmento) = O(hasta− desde + 1)
I peor caso: desde == 0 y hasta == |a| − 1
I la complejidad es O(|a|) para el peor caso
294
Complejidad de UpsortI el ciclo de Upsort itera n veces
I empieza con actual == n − 1I termina con actual == 0I en cada iteracion decrementa actual en uno
I una iteracion haceI una busqueda de maxPosI un swapI un decremento
I de estos pasos, el unico que no es O(1), constante, es elprimero
I ¿cuantos pasos hace maxPos en cada iteracion?I siempre O(hasta− desde + 1) = O(actual + 1)I en la primera hace n (busca el maximo del segmento a ordenar)I en la segunda hace n − 1 (busca el segundo mayor)I en la tercera hace n − 2I y ası (podrıamos verlo por induccion) hasta 2
I el total de pasos esO(n + (n − 1) + . . .+ 2) = O(n ∗ (n + 1)/2− 1) = O(n2)
I los mejores algoritmos de ordenamiento son O(n log n)295