introalg:taller09_4
Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
introalg:taller09_4 [2009/04/19 23:18] – laura | introalg:taller09_4 [2018/08/10 03:03] (actual) – editor externo 127.0.0.1 | ||
---|---|---|---|
Línea 1: | Línea 1: | ||
- | ====== | + | ====== |
- | =====Comparación | + | =====Tipos de datos y comparación |
- | //ordena (x,y)//, //ordena :: (Int,Int) -> (Int,Int)// que, dados dos enteros, los ordena de menor a mayor. | + | ====Tipos==== |
- | //ambospositivos x y//, //ambospositivos :: Int -> Int -> Bool//, que dados dos enteros devuelve //True// si los dos son positivos. | + | En la clase anterior vimos que haskell es un lenguaje tipado. La definición de una función suele venir siempre precedida de su //signatura de tipos//, que tiene la siguiente forma: |
+ | < | ||
+ | nombreFunción :: TipoArgumento1 -> TipoArgumento2 ... -> TipoArgumentoN -> TipoResultado | ||
+ | </code> | ||
+ | Por ejemplo: | ||
+ | < | ||
+ | ordena :: (Int,Int) -> (Int,Int) | ||
+ | ambospositivos :: Int -> Int -> Bool | ||
+ | edad :: (Int, Int, Int) -> (Int, Int, Int) -> Int | ||
+ | cabeza :: [a] -> a | ||
+ | cola :: [a] -> [a] | ||
+ | esVaciaOPrimer0 :: [Int] -> Bool | ||
+ | segundoEsSegundo :: (Int,Int) -> [Int] -> Bool | ||
+ | recortaDia :: [Char] -> [Char] | ||
+ | </code> | ||
+ | En estos ejemplos hemos visto los tipos más básicos con los que vamos a trabajar en la clase: enteros '' | ||
- | //edad :: (Int, Int, Int) -> (Int, Int, Int) -> Int// que dadas dos fechas indica los años transcurridos entre ellas. Por ejemplo edad (20, | + | ====Patrones==== |
- | Suponer (o comprobar con un módulo o definición | + | Vimos también que en haskell se puede expresar muy naturalmente la definición |
+ | < | ||
+ | nombreFunción arg1 arg2 arg3 | patron1 = resultado1 | ||
+ | | patron2 = resultado2 | ||
+ | | patron3 = resultado3 | ||
+ | ... | ||
+ | </ | ||
+ | Los patrones son expresiones booleanas, que en general involucran a las variables a las que se asigna nombre al definir la función ('' | ||
+ | < | ||
+ | nombreFunción arg1 arg2 arg3 | arg1 + arg2 == arg3 = arg3 | ||
+ | | arg1 - arg2 == arg3 = (-arg3) | ||
+ | | arg1 + arg2 /= arg3 = error "el tercer argumento no es ni la suma ni la resta de los dos primeros" | ||
+ | </ | ||
+ | |||
+ | Para resolver una expresión, el intérprete de haskell funciona de forma muy parecida al de prolog: dada una expresión, el intérprete busca una definición de función con la que pueda unificar, de la misma forma que el intérprete de prolog buscaba una regla con cuya cabeza pudiera unificar la expresión. Luego, si esta definición tiene patrones, el intérprete se fija si esos patrones evalúan a '' | ||
- | //cabeza xs//, //cabeza :: [a] --> a//, que devuelve el primer elemento | + | Veamos algunos ejemplos de definición de funciones mediante análisis por casos en patrones, y luego veamos cómo funcionan los mecanismos |
- | //cola xs//, // | + | La función '' |
+ | < | ||
+ | ordena :: (Int,Int) -> (Int,Int) | ||
+ | ordena (x,y) | x < y = (x,y) | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | Veamos que hemos hecho una sola definición para la función, en la que hemos asignado nombres de variables a su argumento, llamando '' | ||
- | // | + | Dada una expresión como '' |
- | // | + | Fíjense que habría otras formas de definir la función, más legibles, más cortas |
+ | < | ||
+ | ordena' | ||
+ | ordena' | ||
+ | | x > y = (y,x) | ||
+ | </code> | ||
+ | Efectivamente, | ||
- | //recortaDia xs//, //recortaDia | + | La función '' |
- | Ayuda: fíjense que el resultado | + | < |
+ | ambospositivos :: Int -> Int -> Bool | ||
+ | ambospositivos x y | x >= 0 && y >= 0 = True | ||
+ | | x >= 0 && y < 0 = False | ||
+ | | x < 0 && y >= 0 = False | ||
+ | | x < 0 && y < 0 = False | ||
+ | </code> | ||
+ | Si nos fijamos un poco, vemos que los tres últimos patrones se pueden colapsar en uno solo, usando un " | ||
+ | < | ||
+ | ambospositivos' | ||
+ | ambospositivos' | ||
+ | | x >= 0 || y < 0 = False | ||
+ | </code> | ||
+ | Pero además, vemos que también podemos expresar lo mismo de otra forma: que nos devuelva '' | ||
+ | < | ||
+ | ambospositivos'' | ||
+ | ambospositivos'' | ||
+ | | otherwise | ||
+ | </ | ||
+ | Pero si reflexionamos bien bien sobre el asunto, nos damos cuenta de que en realidad, si el resultado que buscamos es un booleano, ya lo teníamos, delante de nuestros ojos: el primer patrón es un booleano que evalúa a '' | ||
+ | < | ||
+ | ambospositivos''' | ||
+ | ambospositivos''' | ||
+ | </code> | ||
+ | |||
+ | La función '' | ||
+ | < | ||
+ | edad :: (Int, Int, Int) -> (Int, Int, Int) -> Int | ||
+ | edad (diaN, | ||
+ | | otherwise | ||
+ | </code> | ||
+ | |||
+ | Las funciones '' | ||
+ | < | ||
+ | cabeza | ||
+ | cabeza (x:xs) = x | ||
+ | </ | ||
+ | |||
+ | Pero qué pasa si decimos '' | ||
+ | < | ||
+ | cabeza' | ||
+ | cabeza' | ||
+ | cabeza' | ||
+ | </ | ||
+ | |||
+ | Fíjense que para la definición de '' | ||
+ | < | ||
+ | cabeza'' | ||
+ | cabeza'' | ||
+ | cabeza'' | ||
+ | </ | ||
+ | |||
+ | Pero incluso podemos usar el patrón irrefutable | ||
+ | < | ||
+ | cabeza''' | ||
+ | cabeza''' | ||
+ | cabeza''' | ||
+ | </ | ||
+ | Pero esta última solución presenta el inconveniente | ||
+ | |||
+ | En la definición de '' | ||
+ | < | ||
+ | cola :: [a] -> [a] | ||
+ | cola (_:xs) = xs | ||
+ | cola [] = [] | ||
+ | </ | ||
+ | Como el tipo del resultado | ||
=====Modularización===== | =====Modularización===== | ||
Para hacer un programa que resuelva un problema complejo, lo mejor es analizar bien el problema para dividirlo en partes más chicas. Solucionaremos estas partes más chicas cada una por separado, comprobando que efectivamente cada una hace lo que esperamos que haga. Finalmente, las uniremos para llevar a cabo la tarea compleja que nos planteábamos al principio. A esta estrategia se la conoce como // | Para hacer un programa que resuelva un problema complejo, lo mejor es analizar bien el problema para dividirlo en partes más chicas. Solucionaremos estas partes más chicas cada una por separado, comprobando que efectivamente cada una hace lo que esperamos que haga. Finalmente, las uniremos para llevar a cabo la tarea compleja que nos planteábamos al principio. A esta estrategia se la conoce como // | ||
+ | |||
+ | ==== ej.1: '' | ||
+ | |||
+ | Por ejemplo, para la función de la edad tenemos que suponer que las fechas están siempre bien formadas y que la primera es menor o igual a la segunda. Podríamos incluir esas comprobaciones dentro de la función '' | ||
+ | < | ||
+ | edad' :: (Int, Int, Int) -> (Int, Int, Int) -> Int | ||
+ | edad' (diaN, | ||
+ | | compruebaParametros (diaN, | ||
+ | </ | ||
+ | Podemos usar nombres más cortos para que quede toda la función dentro de una línea, pero entonces nos resultará más difícil recordar qué representa cada variable o función: | ||
+ | < | ||
+ | e :: (Int, Int, Int) -> (Int, Int, Int) -> Int | ||
+ | e (d,m,a) (f,n,b) | c (d,m,a) (f,n,b) && (m < n || (m == n && d < f) = ( b - a ) - 1 | ||
+ | | c (d,m,a) (f, | ||
+ | </ | ||
+ | |||
+ | Veamos ahora cómo podríamos plantear la función '' | ||
+ | < | ||
+ | compruebaParametros :: (Int, Int, Int) -> (Int, Int, Int) -> Bool | ||
+ | compruebaParametros (diaN, | ||
+ | </ | ||
+ | |||
+ | Pero cuidado! esta función permitiría un día 31 de febrero! cómo podemos hacerla más específica? | ||
+ | < | ||
+ | compruebaParametros' | ||
+ | compruebaParametros' | ||
+ | | mesN == 2 = diaN > 0 && diaN <= 28 && diaA > 0 && diaA <= 31 && mesA > 0 && mesA <= 12 && anioN <= anioA = True | ||
+ | | mesA == 2 = diaN > 0 && diaN <= 31 && diaA > 0 && diaA <= 28 && mesN > 0 && mesN <= 12 && anioN <= anioA = True | ||
+ | | otherwise | ||
+ | </ | ||
+ | |||
+ | Y qué pasa con los meses que tienen 30 días y no 31? y con los años bisiestos? la función se complica... si queremos que resulte legible y modificable, | ||
+ | < | ||
+ | compruebaParametros'' | ||
+ | compruebaParametros'' | ||
+ | |||
+ | compruebaMesDia :: (Int, | ||
+ | compruebaMesDia (mes, | ||
+ | | mes == 4 || mes == 6 || mes == 9 || mes == 11 = dia > 0 && dia <= 30 | ||
+ | | otherwise | ||
+ | |||
+ | compruebaAnterior :: (Int, Int, Int) -> (Int, Int, Int) -> Bool | ||
+ | compruebaAnterior (diaN, | ||
+ | | anioA > anioN = True | ||
+ | | otherwise | ||
+ | </ | ||
+ | |||
+ | Como ven, un problema complejo resulta mucho más fácil de abordar si hacemos el esfuerzo inicial de analizarlo con detenimiento, | ||
+ | |||
+ | ==== ej.2: supermercado ==== | ||
Imaginemos un elaborado sistema de descuentos en un supermercado. Por ejemplo, imaginemos que el super hace un descuento del 20% en el monto total si hacemos una compra de 257 pesos, de 350 pesos, de 489 pesos o mayor a 1000. En cambio, si hacemos una compra menor a 100 pesos el descuento es del 5%. Si el monto está entre 200 y 500 pesos, pero no es ninguno de los anteriores, el descuento es del 10%, si es mayor a 500 pero menor a 1000, el descuento es del 15%, excepto si se trata de una compra de 768 pesos, de 999 o de 573, que tienen un descuento del 20 por ciento. Además, los descuentos también dan puntos: los del 20% aportan un número de puntos 10 veces mayor al monto total de la compra, los del 15% aportan un número de puntos 5 veces mayor al monto total de la compra, los del 10% aportan el doble de puntos que el monto de la compra, y si la compra es menor a 200 pesos, el número de puntos es igual al monto de la compra. Por último, si compramos con tickets, en un descuento del 20% nos devuelven el 10% del monto total, en un descuento del 15% nos devuelven el 5% y en un descuento del 10% nos devuelven en 2%. La devolución en tickets se devuelve en tickets, no se aplica un descuento al monto total a pagar. | Imaginemos un elaborado sistema de descuentos en un supermercado. Por ejemplo, imaginemos que el super hace un descuento del 20% en el monto total si hacemos una compra de 257 pesos, de 350 pesos, de 489 pesos o mayor a 1000. En cambio, si hacemos una compra menor a 100 pesos el descuento es del 5%. Si el monto está entre 200 y 500 pesos, pero no es ninguno de los anteriores, el descuento es del 10%, si es mayor a 500 pero menor a 1000, el descuento es del 15%, excepto si se trata de una compra de 768 pesos, de 999 o de 573, que tienen un descuento del 20 por ciento. Además, los descuentos también dan puntos: los del 20% aportan un número de puntos 10 veces mayor al monto total de la compra, los del 15% aportan un número de puntos 5 veces mayor al monto total de la compra, los del 10% aportan el doble de puntos que el monto de la compra, y si la compra es menor a 200 pesos, el número de puntos es igual al monto de la compra. Por último, si compramos con tickets, en un descuento del 20% nos devuelven el 10% del monto total, en un descuento del 15% nos devuelven el 5% y en un descuento del 10% nos devuelven en 2%. La devolución en tickets se devuelve en tickets, no se aplica un descuento al monto total a pagar. | ||
Línea 82: | Línea 240: | ||
</ | </ | ||
- | Tomemos como ejemplo la función | + | ==== ej.3: '' |
+ | |||
+ | Tomemos como ejemplo la función | ||
| | ||
Suponer el siguiente juego: m jugadores en ronda comienzan a decir los números naturales consecutivamente. | Suponer el siguiente juego: m jugadores en ronda comienzan a decir los números naturales consecutivamente. | ||
Cuando toca un multiplo de 7 o un número con algún dígito igual a 7, el jugador debe decir pip en lugar del número. | Cuando toca un multiplo de 7 o un número con algún dígito igual a 7, el jugador debe decir pip en lugar del número. | ||
- | Se pide: encontrar un predicado | + | Se pide: encontrar un predicado |
- | Con un complicado juego de //mod// y //div// podemos llegar a una solución: | + | Con un complicado juego de '' |
< | < | ||
Línea 117: | Línea 277: | ||
True | True | ||
- | Y hasta podemos generalizar las funciones //unidad, decena, etc.// en una que nos dé el dígito n-ésimo de la representación decimal, pero para eso necesitamos usar **funciones recursivas**.\\ | ||
- | Eso será tema de la próxima clase, pero veamos de todas maneras la " | ||
- | |||
- | < | ||
- | digitos :: Int -> [Int] | ||
- | digitos 0 = [] | ||
- | digitos n = n `mod` 10 : (digitos (n `div` 10)) | ||
- | </ | ||
- | |||
- | La probamos para ver como opera | ||
- | |||
- | Main> digitos 0 | ||
- | [] | ||
- | Main> digitos 1 | ||
- | [1] | ||
- | Main> digitos 10 | ||
- | [0,1] | ||
- | Main> digitos 120 | ||
- | [0,2,1] | ||
- | Main> digitos 123123123 | ||
- | [3, | ||
- | |||
- | Veamos ahora el ejercicio // | ||
=====Definiciones locales===== | =====Definiciones locales===== | ||
- | A veces queremos modularizar pero queremos que los módulos sólo estén disponibles para el programa que estamos haciendo en este momento, y no para todo el resto. Por ejemplo, si queremos definir la función "'' | + | A veces queremos modularizar pero queremos que los módulos sólo estén disponibles para el programa que estamos haciendo en este momento, y no para todo el resto. Por ejemplo, si queremos definir la función "'' |
Podemos definir módulos (en el caso de haskell, funciones) de forma que sólo las pueda usar un determinado programa, definiéndolas localmente. Vimos un ejemplo de esto en la clase anterior, para definir la función que calcula el área de un prisma: | Podemos definir módulos (en el caso de haskell, funciones) de forma que sólo las pueda usar un determinado programa, definiéndolas localmente. Vimos un ejemplo de esto en la clase anterior, para definir la función que calcula el área de un prisma: | ||
Línea 169: | Línea 306: | ||
ERROR " | ERROR " | ||
</ | </ | ||
- | |||
- | Funciones locales vs. globales | ||
===== Ejercicios ===== | ===== Ejercicios ===== | ||
+ | Si no lo hicieron el día anterior, traten de resolver los siguientes problemas: | ||
+ | |||
+ | - '' | ||
+ | - '' | ||
+ | - '' | ||
+ | - Definan, según su propio criterio para otorgar becas, la función '' | ||
+ | |||
+ | Nuevos problemas: | ||
+ | |||
+ | - Definir una función '' | ||
+ | - Definir la función '' | ||
+ | |||
+ | Viejos problemas, nuevas soluciones: re-piensen los problemas difíciles que planteamos en la segunda clase de prolog usando modularización: | ||
+ | |||
+ | - ¿Cómo sería un programa para recomendar amigos en una red social tipo // | ||
+ | - Tenemos una lista de las conexiones por tren entre pares de ciudades, por ejemplo '' |
introalg/taller09_4.1240183111.txt.gz · Última modificación: 2018/08/10 03:03 (editor externo)