Introducción. Haskell

Documentos relacionados
Programación Declarativa Haskell Informática Sistemas Curso Pepe Gallardo Universidad de Málaga. Tema 8. Listas

Tema II: Introducción al Lenguaje Funcional

Programación Funcional

Informática Haskell Matemáticas Curso Pepe Gallardo Universidad de Málaga. Tema 8. Listas

Introducción a Haskell. Cecilia Manzino

Tipos en Haskell. Cecilia Manzino

Tema 3: Tipos y clases

Tema 8: Funciones de orden superior

Tema 3: Tipos y clases

Tema 8: Funciones de orden superior

Guía 2: Listas, recursión e inducción

data Tree a = Tip Node a (Tree a) (Tree a) deriving Show

Tema 3: Tipos y clases

n! = 1 2 n 0! = 1 (n+1)! = (n + 1) n!

Informática de 1 o de Matemáticas. Ejercicios de Introducción a Haskell. Ejercicio 1 Consideremos la siguiente definición:

Introducción a OCaml. October 19, 2015

Introducción a Haskell. El lenguaje Haskell

El sistema de clases de Haskell. Introducción

Programación Funcional

Algoritmos y programas. Algoritmos y Estructuras de Datos I

Práctica N o 1 - Programación Funcional

Tema 2. Tipos predefinidos

Laboratorio Análisis Lógico Práctica 2: Tipos de datos en Haskell

Listas y Recursión. Taller de Álgebra I. Primer Cuatrimestre de 2015

Guía 2: Funciones, listas, recursión e inducción

Tipos algebraicos y abstractos. Algoritmos y Estructuras de Datos I. Tipos algebraicos

Tema 4: Definición de funciones

Tema 3. Patrones y Definiciones de Funciones

Tema 4: Definición de funciones

Tema 9: Declaraciones de tipos y clases

Tema 2: Introducción a Haskell

Programación Funcional en Haskell

Metodologías de Programación II Introducción a OCaml

EJERCICIOS DE LENGUAJES Y PARADIGMAS DE PROGRAMACIÓN (CUESTIONES DE EXAMEN) PROGRAMACIÓN FUNCIONAL

Tema 6: Funciones recursivas

Programación declarativa ( )

UNIDAD IV Programación Funcional. Lic. Jesús Germán Andrés PAUTSCH - FCEQyN - UNaM

Informática Haskell Matemáticas Curso Pepe Gallardo Universidad de Málaga. Temario

Programación Declarativa Haskell Informática Sistemas Curso Pepe Gallardo Universidad de Málaga

Ejercicios de programación funcional con Haskell

Isabelle como un lenguaje funcional

Práctica 1. Programación Funcional Introducción a Helium

CAPÍTULO IV: 4.1 Introducción a la. Programación Funcional

Guía 2: Funciones, listas, recursión e inducción

Lenguajes funcionales: λ-cálculo

1.1 Define un operador ( ) que devuelva True si y solo si tan solo uno de sus argumentos es True. Por ejemplo:? (3 > 2) (2 > 5)

Ejercicios de programación funcional con Haskell

Tema 12: Programas interactivos

Guía 1: Funciones, precedencia y tipado

Lenguajes de Programación Programación funcional

Tema 7. El sistema de clases

Tema 5: Definiciones de listas por comprensión

Para entender la recursividad primero tenemos que entender la recursividad

Tema 5: Definiciones de listas por comprensión

Tutorial Haskell David Julián Guzmán Cárdenas Cristian Alexanther Rojas Cárdenas Luis Ernesto Gil Castellanos

Tipos de datos algebraicos

Tema 1 INTRODUCCIÓN A LOS LENGUAJES DE PROGRAMACIÓN

Generación de Código Intermedio

Componentes Básicos. InCo. InCo Componentes Básicos 1 / 28

Laboratorio Análisis Lógico Práctica 1

Lógica y Programación

Programación Declarativa UNIVERSIDAD DE MÁLAGA

Programación Funcional Haskell Clase 21

Guía 2: Funciones, listas, recursión e inducción

Exámenes de Programación funcional con Haskell ( )

Introducción a la programación. Cecilia Manzino

Tema 1. Tema 2. Informática Haskell Matemáticas Curso Pepe Gallardo Universidad de Málaga

Introducción a la Programación Genérica

Programación Funcional Haskell Clase 19

Tema 3. Tipos de datos simples

Manos a la obra: Recursión, división y listas

8 INTRODUCCIÓN AL OBJECTIVE CAML

Unidad Didáctica 2. Elementos básicos del lenguaje Java Tipos, declaraciones, expresiones y asignaciones

Un calculadora avanzada... y algo más!

Lenguajes de Programación. Capítulo 4. Expresiones.

Comprender las diferencias entre tipos de datos primitivos similares, y aprender a elegir el tipo más conveniente en cada caso.

Apunte Laboratorio ALPI - El lenguaje de programación Pascal

GUÍA BÁSICA DE SCHEME v.4

Estructura de un programa en Java. Tipos de datos básicos. class miprimerprograma{ // comentario, no es parte del programa

Paradigmas de lenguajes de programación. Introducción a la programación imperativa. Lenguaje C. Programación imperativa

Tema 13: Programas interactivos

Transcripción:

Introducción Haskell 1

Valores, Tipos, Expresiones Los valores son entidades abstractas que podemos considerar como la respuesta a un cálculo 5-1 8 Cada valor tiene asociado un tipo (<valor> :: <tipo>) 2 :: Int Int {...-3,-2,-1,0,1,2,3...} Las expresiones son términos construidos a partir de valores (2*3)+(4-5) La reducción o evaluación de una expresión consiste en aplicar una serie de reglas que transforman la expresión en un valor (2*3)+(4-5) 6+(4-5) 6+(-1) 6-1 5 2

Valores, Tipos, Expresiones (II) Un valor es una expresión irreducible Toda expresión tiene un tipo: el tipo del valor que resulta de reducir la expresión (Sistema Fuertemente Tipado). (2*3)+(4-5) :: Int El tipo de una expresión debe establecerse en tiempo de compilación sin evaluar la expresión (Tipado Estático) El compilador calcula el tipo de una expresión, normalmente, sin ninguna anotación del programador (Inferencia de Tipos) Las funciones también son valores (y por lo tanto tendrán tipo) 3

Tipos Básicos Int, subconjunto de los enteros (normalmente, los enteros representables por una palabra del procesador): 2, -1... Integer, enteros (precisión absoluta) Char, caracteres: a, 5, \n, \xf4... Float/Double, números en punto flotante de simple/doble precisión: 3.14159, 2, 2.5e+2... String, cadenas de caracteres: "hola",... Bool, booleanos: True, False 4

Definiciones Asociamos identificador y expresión (<id> = <expr>) x = (2*3)+(4-5) y = x^2 Opcionalmente, podemos incluir una anotación de tipo: x :: Int x = (2*3)+(4-5) A pesar de que son inferidas automáticamente, las anotaciones de tipo se suelen añadir para ciertas definiciones por: Documentación Localización de errores Facilitar optimización (sobrecarga) 5

Definición local let...in Definición local: let <id> = <e 1 > in <e 2 > let x = 2*3 in x+1 La definición local es una expresión, cuyo tipo es el de e 2 (let x = 2*3 in x+1) + 5 Semántica: Sustituimos id por su definición e 1 en e 2 let x = 2*3 in x+1 x+1[2*3/x] (2*3)+1 7 Ámbito de la definición let x = 1 in let y = 2 in x+y y x 6

Definición local where Definición local: <e 2 > where <id> = <e 1 > x+1 where x = 2*3 Ambigüedad entre let...in y where x where let x = 5 in x*x x let...in where x = 6 Se permite definir múltiples identificadores simultáneamente: let x = 5 x+y where x = 5 y = 6 y = 6 in x+y 7

Regla del Contexto Las declaraciones de una definición local, se delimitan con llaves ({}), y punto y coma (;) a+b where { a=5; b=4 } Opcionalmente, podemos usar el contexto como delimitador a+b where a=5 b=4 El contexto se puede usar en cualquier lista de declaraciones de let..in, where, y case let y = 7 x = y where y = 8 z = 2 in y+x+z z = 4 8

Expresión Condicional Expresión Condicional: if <e 1 > then <e 2 > else <e 3 > Semántica: if 2+3==5 then "ho. el se "la" Si e 1 True entonces if... e 2 Si e 1 False entonces if... e 3 if 2+3==5 then ho else la ho La guardia de la expresión condicional debe ser de tipo Bool Las ramas then y else deben tener igual tipo El tipo de la expresión condicional es el tipo de la rama then 9

Funciones Función matemática: Transparencia Referencial (f x = f x) Definición: <función> <argumento> = <expresión> sucesor x = x+1 cuadrado y = y*y Una función que toma como argumento algo de tipo T 1 y lo transforma en algo de tipo T 2 tiene por tipo T 1 T 2 sucesor :: Int ->Int La aplicación de una función f a e (f e) se define como la sustitución del argumento de f por e en el cuerpo de f sucesor 5 x+1[5/x] 5+1 6 10

Funciones (II) Todas las definiciones dentro del mismo contexto son mutuamente recursivas: f x = g (x-1) g x = if x == 0 then 0 else f (x-1) f 3 g 2 f 1 g 0 0 f 4 g 3 f 2 g 1 f 0 g (-1)... Si f no está definida en x, decimos que f x = Función anónima: \<arg> -> <expr> (\x ->x+2) 4 x+2[4/x] 4+2 6 La definición de funciones es una ayuda sintáctica: sucesor x = x+1 sucesor = \x ->x+1 11

Guardias Ayuda sintáctica para la definición de funciones: <función> <argumento> <guardia 1 > = <expr 1 > <guardia 2 > = <expr 2 >.. <guardia n > = <expr n > Las guardias deben ser de tipo Bool; las expr i tienen que ser del mismo tipo (imagen de la función) Las guardias se evalúan de arriba a abajo, hasta encontrar una guardia i que sea True la función devuelve expr i otherwise (o True) puede usarse como caso por defecto fact n n == 0 = 1 otherwise = n * fact (n-1) 12

Funciones de Orden Superior Funciones que reciben como argumento una función o que devuelven una función como resultado: Función como argumento: f1 :: (Int -> Int) -> Int f1 g = 2 + g 3 Función como resultado: f2 :: Int -> (Int -> Int) f2 x = \y -> x+y Función como argumento y como resultado: f3 :: (Int -> Int) -> (Int -> Int) f3 g = \y -> 2 * g (y+1) 13

Funciones de Orden Superior (II) Diferentes formas de escribir lo mismo f = \x -> (\y -> x+y) f = \x -> \y -> x+y f x = \y -> x+y f x y = x+y f = \x y -> x+y (f 3) es una función Int ->Int que dado y, devuelve 3+y. (f 3) 4 ((\x ->x+y) 3) 4 (\y ->3+y) 4 3+4 7 La aplicación asocia por la izquierda: (f 3) 4 f 3 4 El constructor de tipo -> asocia por la derecha: Int ->(Int ->Int) Int ->Int ->Int (Int ->Int) ->Int Int ->Int ->Int 14

Funciones con Varios Argumentos Todas las funciones tienen un único argumento (Dominio) y devuelven un único resultado (Imagen) Una función que devuelve una función como resultado, puede considerarse como una función de dos argumentos: f :: Int ->Int ->Int f 3 4 7 f x y = x+y f 3 \y ->3+y (Aplicación parcial) Si es un operador infijo, ( ) es una función prefija equivalente a+b (+) a b Si f es una función de 2 argumentos, f es un operador infijo equivalente mod a b a mod b 15

Pattern-Matching Definir ecuaciones basadas en la forma del argumento Forma más simple: constantes e identificadores fact 0 = 1 fact n = n * fact (n-1) Comprueba de arriba a abajo hasta encontrar el patrón que encaje Una constante sólo encaja con un argumento igual Un identificador encaja con cualquier argumento El comodín _ hace las veces de identificador Se puede combinar con las guardias fib 0 = 1 fib 1 = 1 fib n n > 1 = fib (n-2) + fib (n-1) otherwise = error "fib de un número negativo" 16

Producto cartesiano Tipo Producto (Tuplas) A = {1, 2, 3} B = {a, b} A B = {(1, a), (1, b), (2, a), (2, b), (3, a), (3, b)} Si a :: T 1 y b :: T 2 entonces (a,b) :: (T 1,T 2 ) Generalizable para tuplas de aridad n > 2 (True, "hola", \x -> x ++ ".") :: (Bool, String, String -> String) Funciones sobre tuplas: Uso de pattern-matching mayor :: (Int,Int,Int) -> Int mayor (x,y,z) x > y && x > z = x y > x && y > z = y otherwise = z Los componentes de una tupla pueden ser otras tuplas ((1,2),3) :: ((Int,Int),Int) (1,(2,3)) :: (Int,(Int,Int)) (1,2,3) :: (Int,Int,Int) 17

Tipo Producto (Tuplas) (II) Un uso: implementar funciones de varios argumentos suma :: (Int,Int) -> Int suma (x,y) = x+y Otro uso común: Funciones que devuelven varios resultados sumayproducto :: Int -> Int -> (Int,Int) sumayproducto m n m > n = (0,1) otherwise = let (s,p) = sumayproducto (m+1) n in (m+s, m*p) El uso de sinónimos de tipo simplifica las anotaciones de tipo type Jugador = (String, String, Int) roberto :: Jugador roberto = ("Roberto Carlos", "Brasil", 3) nacionalidad :: Jugador -> String nacionalidad (_,pais,_) = pais 18

Polimorfismo El mismo código puede ser utilizado con distintos tipos Distinguimos tipos (con mayúsculas) de variables de tipo (con minúsculas) pueden ser reemplazadas por cualquier tipo El tipo de id dada su definición id x = x es a ->a. La variable de tipo a puede reemplazarse por cualquier tipo válido, dando lugar a todos las posibles usos de id: Sustitución de a Int (Int,Int) Int ->Int. Tipo resultante para id Int ->Int (Int,Int) ->(Int,Int) (Int ->Int) ->(Int ->Int). Un tipo e que contiene una variable de tipo a (esquema de tipo) es realmente a tipos. e 19

Polimorfismo (II) Polimorfismo + Funciones de Orden Superior = Abstracción prod :: (a -> b) -> (c -> d) -> (a,c) -> (b,d) prod (f,g) = par (f.fst, g. snd) sumayproducto :: Int -> Int -> (Int,Int) sumayproducto m n m > n = (0,1) otherwise = prod (+m) (*m) (sumayproducto (m+1) n) Si es un operador binario, ( a) es (\x ->x a) y (a ) es (\x ->a x) (secciones) Composición de funciones: operador. (predefinido) (.) :: (a -> b) -> (c -> a) -> (c -> b) f. g = \x -> f (g x) f = (+1). (^2) 20

Currificación Dos formas de representar funciones multiargumento: Currificada: Utilizando funciones de orden superior suma :: Int -> Int -> Int suma x y = x+y Decurrificada: Utilizando funciones sobre tuplas suma :: (Int,Int) -> Int suma (x,y) = x+y Preferencia: currificadas (por el uso de aplicación parcial) Es posible transformar un formato en el otro (predefinidas) curry :: ((a,b) -> c) -> (a -> b -> c) curry f = \x -> \y -> f (x,y) uncurry :: (a -> b -> c) -> ((a,b) -> c) uncurry f = \(x,y) -> f x y El término currificación se establece en honor a Haskell B. Curry 21

Listas Colección ordenada de valores homogéneos Si a 1 ::T, a 2 ::T,..., a n ::T entonces [a 1,a 2,...,a n ] :: [T ] Algunos ejemplos: [1,2,3] :: [Int] [ (True,1), (False,2) ] :: [(Bool,Int)] [ (+1), (^2), (\x -> x) ] :: [Int -> Int] [ [1,2,3], [4], [5,6] ] :: [[Int]] Las listas se crean a partir de dos constructores: [] :: [a], la lista vacía (:) :: a ->[a] ->[a], constructor infijo que a partir de un elemento y una lista construye una lista nueva, resultado de pegar el elemento en la cabeza de la lista 1:(2:(3:[])) 1:2:3:[] [1,2,3] a:(b:c) (a:b):c 22

Listas (II) Funciones sobre listas: pattern-matching null :: [a] -> Bool null [] = True null _ = False -- predefinida head :: [a] -> a head (x:_) = x tail :: [a] -> [a] tail (_:xs) = xs -- predefinida -- predefinida last :: [a] -> a last (x::xs) = if null xs then x else last xs init :: [a] -> [a] init (x:xs) = in null xs then [] else init xs concat :: [[a]] -> [a] concat [] = [] concat (xs:xss) = xs ++ concat xss Herramienta fundamental: inducción 23

sum :: [Int] -> Int sum [] = 0 sum (x:xs) = x + sum xs -- predefinida sum :: [Int] -> Int sum xs = if xs == [] then 0 else head xs + sum (tail xs) 24

Funciones sobre listas (II) reverse :: [a] -> [a] reverse [] = [] reverse (x:xs) = reverse xs ++ [x] length :: [a] -> Int length [] = 0 length (_:xs) = 1 + length xs -- predefinida (++) :: [a] -> [a] -> [a] -- predefinida [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys) take :: Int -> [a] -> [a] take 0 xs = [] take _ [] = [] take n (x:xs) = x: take (n - 1) xs drop :: Int -> [a] -> [a] drop 0 xs = xs drop _ [] = [] drop n (x:xs) = drop (n - 1) xs 25

Funciones sobre listas (III) (!!) :: [a] -> Int -> a (x:xs)!! 0 = x (x:xs)!! n = xs!! (n - 1) map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = fx : map f xs filter :: (a -> Bool) -> [a] -> [a] filter p [] = [] filter p (x:xs) = if p x then x: filter p xs else filter p xs zip :: [a] -> [b] -> [(a,b)] zip [] _ = [] zip _ [] = [] zip (x:xs) (y:ys) = (x,y) : zip xs ys zipwith :: (a -> b -> c) -> [a] -> [b] -> [c] zipwith f xs ys = map (uncurry f) (zip xs ys) 26

Funciones sobre listas (IV) zipp :: ([a], [b]) -> [(a,b)] zipp = uncurry zip zippwith :: (a -> b -> c) -> ([a], [b]) -> [c] zippwith f = map (uncurry f). zipp par (f,g) x = (f x, g x) unzip :: [(a,b)] -> ([a],[b]) unzip = par (map fst, map snd) 27

La familia de funciones fold foldr :: (a -> b -> b) -> b -> [a] -> b foldr f e [] = e foldr f e (x:xs) = f x (foldr f e xs) foldl :: (b -> a -> b) -> b -> [a] -> b foldl f e [] = e foldl f e (x:xs) = foldl f (f e x) xs foldr1 :: (a -> a -> a) -> [a] -> a foldr1 f (x:xs) = if null xs then x else f x (foldr1 f xs) foldl1 :: (a -> a -> a) -> [a] -> a foldl1 f (x:xs) = foldl f x xs 28

concat :: [[a]] -> [a] concat = foldr (++) [] Definiciones con foldr reverse :: [a] -> [a] reverse = foldr snoc [] where snoc x xs = xs ++ [x] length :: [a] -> Int lenght = foldr increm 0 where increm x n = 1 + n sum :: Num a => [a] -> a sum = foldr (+) 0 and :: [Bool] -> Bool and = foldr (&&) True map :: (a -> b) -> [a] -> [b] map f = foldr (cons. f) [] where cons x xs = x:xs 29

Definiciones con foldr (II) maxlist :: (Ord a, Bounded a) => [a] -> a maxlist = foldr ( max ) minbound unzip :: [(a,b)] -> ([a],[b]) unzip = foldr conss ([],[]) where conss (x,y) (xs, ys) = (x:xs, y:ys) 30

inits & tails inits :: [a] -> [[a]] inits [] = [[]] inits (x:xs) = [] : map (x:) (inits xs) tails :: [a] -> [[a]] tails [] = [] tails (x:xs) = (x:xs) : tails xs 31

La familia de funciones scan scanl :: (a -> b -> a) -> a -> [b] -> [a] scanl f e = map (foldl f e). inits scanl :: (a -> b -> a) -> a -> [b] -> [a] -- eficiente scanl f e [] = [e] scanl f e (x:xs) = e : scanl f (f e x) xs scanr :: (a -> b -> b) -> b -> [a] -> [b] scanr f e = map (foldr f e). tails scanr :: (a -> b -> b) -> b -> [a] -> [b] -- eficiente scanr f e [] = [e] scanr f e (x:xs) = f x (heay ys) : ys where ys = scanr f a xs 32

Leyes de take & drop Leyes take m. take n = take (m min n) drop m. drop n = drop (n + m) take m. drop m = drop n. take (m + n) Leyes del map map id = id map (f. g) = map f. map g f. head = head. map f map f. tail = tail. map f map f. reverse = reverse. map f map f. concat = concat. map (map f) Leyes del filter filter p. filter q = filter (p and q) x && q x filter p. concat = concat. map (filter p) donde (p and q) x = p 33

Leyes del prod y par Leyes (II) prod(f,g) = par(f.fst, g.snd) prod(f,g).par(h,k) = par(f.h,g.k) par (f,g).h = par(f.h,g.h) Leyes del zip zipp(unzip xys) = xys prod(map f, map g).unzip = unzip.map(prod(f,g)) zipp.prod(map f, map g) = map(prod(f,g)).zipp 34

Teoremas de la dualidad Primer teorema de la dualidad foldr ( ) e xs = foldl ( ) e xs cuando : es asociativo e es su elemento neutro Segundo teorema de la dualidad foldr ( ) e xs = foldl ( ) e xs cuando para todo x, y, y z: x (y z) = (x y) z z e = e x Tercer teorema de la dualidad foldr f e xs = foldl (flip f) e (reverse xs) donde flip f x y = f y x 35

Teorema de fusión para foldr f. foldr g a = foldr h b cuando : f es estricta f a = b Teoremas de fusión f (g x y) = h x (f y) para todo x e y Teorema de fusión para foldl f. foldl g a = foldl h b cuando : f es estricta f a = b f (g x y) = h (f x) y para todo x e y 36

Teorema de fusión fold-map Teoremas de fusión(ii) foldr f a. map g = foldr (f. g) a cuando : foldr f a es estricta Teorema de fusión fold-concat foldr f a. concat = foldr (flip foldr f)) a cuando : foldr f a es estricta Ley del Contable foldr f e.concat=foldr f e.map(foldr f e) cuando : f es asociativa e es su elemento neutro 37

Ley de fusión fold-scan Teoremas de fusión(ii) foldr1 ( ).scanl ( ) e = foldr ( ) e cuando: x y = e ( x y) otimes es asociativo e es su elemento neutro es distributivo con respecto a : x (y z = (x y) (x z) 38

Mayor suma de segmentos Dada una secuencia de números, se pide calcular la mayor de las sumas de todos los segmentos de la secuencia. Por ejemplo, la secuencia: [ -1, 2, -3, 5, -2, 1, 3, -2, -2, -3, 6] tiene como mayor suma 7, que es la suma del segmento [5, -2, 1, 3]. 39

Mayor suma de segmentos(ii) mss :: [Int] -> Int mss = maxlist.map sum.segs segs :: [a] -> [[a]] segs = concat.map inits. tails La evaluación directa de mss necesita un número de pasos proporcional a n 3 para una lista de longitud n. Hay aproximadamente n 2 segmentos y sumar todos ellos necesita aproximadamente n 3 pasos. 40

Mayor suma de segmentos(iii) Sustituimos la definición de segs en mss: maxlist.map sum.concat.map inits.tails usamos la ley del map map f.concat = concat. map (map f) maxlist.concat.map (map sum).map inits.tails usamos la ley del contable foldr f e.concat=foldr f e.map(foldr f e) maxlist.map maxlist..map (map sum).map inits.tails usamos la ley map (f.g) = map f. map g) maxlist.map maxlist.map (map sum.inits).tails usamos la ley map (f.g) = map f. map g) maxlist.map (maxlist.map sum.inits).tails 41

Mayor suma de segmentos(iii) sustituimos map sum.inits = scanl (+) 0 maxlist.map (maxlist.scanl (+) 0).tails dado que maxlist = foldr1 ( max ) y (+) es distributivo con respecto a ( max ) aplicamos la lef de fusión fold-scan maxlist.map (foldr ( ) 0).tails donde x y = 0 max (x + y) sustituimos map (foldr f e). tails = scanr f e maxlist.scanr ( ) 0 El resultado es un programa que calcula la mayor suma de segmentos en tiempo lineal 42

Strings El tipo String es un sinónimo de tipo: type String = [Char] Sintaxis especial para las cadenas de caracteres: [ h, o, l, a ] "hola" "hola "++ [ m, u, n, d, o ] == "hola mundo" amayusculas :: String -> String amayusculas [] = [] amayusculas (x:xs) = toupper x : amayusculas xs 43

Enumeraciones Lista de elementos entre m y n: enumfromto :: Int -> Int -> [Int] -- predefinida enumfromto m n m > n = [] otherwise = m : enumfromto (m+1) n Forma alternativa: enumfromto m n == [m..n] [3..7] == enumfromto 3 7 == [3,4,5,6,7] Otra forma de definir una lista: [m,m + k..n] [m,m + k..n] [m,m + k,m + 2k,...,m + ck] m + ck n < m + (c + 1)k [3,5..10] == enumfromthento 3 5 7 == [3,5,7,9] También vale con Char [ a.. z ] == [ a, b,..., z ] == "abcdefghijklmnopqrstuvwxyz" 44

Comprensión de listas (List Comprehension) Construcción de listas basándonos en otras listas Forma más simple: [expr pat <- lista] pat <- lista es un generador: se recorre lista secuencialmente y se hace pattern-matching con pat. La lista resultado tiene un elemento por cada elemento generado, que se calcula según expr [ 2*x x <- [1..10]] == [2,4,6,8,10,12,14,16,18,20] Es posible más de 1 generador: [expr pat 1 <- l 1,...,pat n <- l n ] [(x,y) x <- [1..3], y <- [True,False]] == [(1,True), (1,False), (2,True), (2,False), (3,True), (3,False)] [(x,y) x <- [1..3], y <- [1..x]] == [(1,1), (2,1), (2,2), (3,1), (3,2), (3,3)] 45

Comprensión de listas (List Comprehension) (II) Guardias para limitar generación: [expr pat <- lista, guardia] [2*x x <- [1..10], x > 5] == [12,14,16,18,20] [(x,y) x <- [1..3], y <- [1..3], x /= y] == [ (1,2), (1,3), (2,1), (2,3), (3,1), (3,2) ] Simplificación de definiciones: menores_que k xs = [ x x <- xs, x < k ] filter p xs = [ x x <- xs, p x ] map f xs = [f x x <- xs] qs [] = [] qs (x:xs) = qs [y y <- xs, y < x] ++ [x] ++ qs [y y <- xs, y >= x] 46

Mónadas El grupo de las operaciones de entrada salida en Haskell se denota como IO (). Una exprosion de tipo IO () denota una acción; cuando se evalúa, la acción se realiza. Escribir un carácter: putchar :: Char -> IO () No hacer nada done :: IO () Combinar dos acciones de E/S (>>) :: IO () -> IO () -> IO () Escribir un String write :: String -> IO () write [] = done write (x:xs) = putchar x >> write xs 47

Mónadas(II) Escribir un String terminado en retorno de carro writeln :: String -> IO () writeln xs = write xs >> putchar \n Leer un carácter getchar :: IO Char Generalización de done return :: a -> IO a Implementación de done done :: IO () done = return () Generalización del tipo de (>>) (>>) :: IO a -> IO b -> IO b si p y q son acciones, entonces p >> q es una acción que, cuando se realice, primero efectúa p, ignora el valor devuelto y despus realiza q. 48

Generalización de (>>) Mónadas(II) (>>=) :: = IO a -> (a -> IO b) -> IO b Leer n caracteres readn :: Iont -> IO String readn 0 = return [] readn n = getchar >>= q where q c = read (n-1) >>= r where r cs = return (c:cs) Clase Monad class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b Notación do readn :: Iont -> IO String readn 0 = return [] readn n = c <- getchar cs <- readn (n-1) return (c:cs) 49

Leyes de las Mónadas return es un elemento neutro por la derecha de >> p >>= return == p return es un elemento neutro por la izquierda de >> en el sentido (return e) >>= q = q e >>= es asociativa en el siguiente sentido (p >>= q) >>= r = p >>= s where s x = (q x >>= r) 50