Programación Funcional en Haskell



Documentos relacionados
Universidad Católica del Maule. Fundamentos de Computación Especificación de tipos de datos ESPECIFICACIÓN ALGEBRAICA DE TIPOS DE DATOS

Profesorado de Informática Ciencias de la Computación INET- DFPD Matemática I - Matemática Discreta usando el computador Ing. Prof.

OPERADORES LÓGICOS Y DE COMPARACIÓN EN PHP. PRIORIDADES. EJEMPLOS. EJERCICIOS RESUELTOS. (CU00818B)

Nota 2. Luis Sierra. Marzo del 2010

En cualquier caso, tampoco es demasiado importante el significado de la "B", si es que lo tiene, lo interesante realmente es el algoritmo.

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

CASO PRÁCTICO DISTRIBUCIÓN DE COSTES

Estructuras de Datos y Algoritmos. Árboles de Expresión

Modulo 1 El lenguaje Java

PRÁCTICA N 2 SISTEMAS DE NUMERACIÓN

LABORATORIO Nº 2 GUÍA PARA REALIZAR FORMULAS EN EXCEL

Índice Introducción Números Polinomios Funciones y su Representación. Curso 0: Matemáticas y sus Aplicaciones Tema 1. Números, Polinomios y Funciones

Centro de Capacitación en Informática

TEMA 5. CONTROL DE FLUJO DEL PROGRAMA. Sentencia Instrucción Expresión Operadores + Operandos Sintaxis: Sentencia ;

UNIDAD 6. POLINOMIOS CON COEFICIENTES ENTEROS

Lección 24: Lenguaje algebraico y sustituciones

Tema 07. LÍMITES Y CONTINUIDAD DE FUNCIONES

MATEMÁTICAS CON LA HOJA DE CÁLCULO

Lección 9: Polinomios

Apuntes de Matemática Discreta 1. Conjuntos y Subconjuntos

Tecnólogo Informático- Estructuras de Datos y Algoritmos- 2009

Tema 1: Fundamentos de lógica, teoría de conjuntos y estructuras algebraicas: Apéndice

Arreglos. // Incluir E/S y Librerías Standard #include <stdlib.h> #include <stdio.h>

Los números racionales

TEMA 3 PROFESOR: M.C. ALEJANDRO GUTIÉRREZ DÍAZ 2 3. PROCESAMIENTO DE CONSULTAS DISTRIBUIDAS

Programa para el Mejoramiento de la Enseñanza de la Matemática en ANEP Proyecto: Análisis, Reflexión y Producción. Fracciones

MÉTODO DEL CAMBIO DE BASE PARA CÁLCULO MANUAL DE SUBREDES CON IP V4.0

Ecuaciones de primer grado con dos incógnitas

Clases de apoyo de matemáticas Fracciones y decimales Escuela 765 Lago Puelo Provincia de Chubut

Lección 4: Suma y resta de números racionales

INTRODUCCIÓN AL TIPO COMPUESTO CADENA CONTENIDOS

NÚMEROS NATURALES Y NÚMEROS ENTEROS

_ Antología de Física I. Unidad II Vectores. Elaboró: Ing. Víctor H. Alcalá-Octaviano

Módulo mod_banners para insertar y visualizar anuncios o publicidad (banners) en Joomla. Contador. (CU00446A)

Módulo 9 Sistema matemático y operaciones binarias

CONSULTAS MULTITABLAS SQL SERVER Manual de Referencia para usuarios. Salomón Ccance CCANCE WEBSITE

Los polinomios. Un polinomio es una expresión algebraica con una única letra, llamada variable. Ejemplo: 9x 6 3x 4 + x 6 polinomio de variable x

La ventana de Microsoft Excel

REPASO NÚMEROS NATURALES Y NÚMEROS ENTEROS

LABORATORIO Nº 3 PRÁCTICA DE FUNCIONES EN MICROSOFT EXCEL

Interpolación polinómica

Teclado sobre una PDA para Personas con Parálisis Cerebral

Índice Introducción Estructuras Algebraicas Listas Los Números Enteros Polinomios. Álgebra y Matemática Discreta Sesión de Prácticas 1

Esta extensión está obsoleta a partir de PHP 5.5.0, y será eliminada en el futuro

FORMACIÓN DE EQUIPOS DE E-LEARNING 2.0 MÓDULO DE DISEÑO Y PRODUCCIÓN DE MATERIALES UNIDAD 6 B

3.2 Operaciones aritmético-lógicas en Pascal

Qué son los monomios?

PHP y MySQL. Indice: Switch Bucles For While do...while

LAS BARRAS. La barra de acceso rápido

Colegio Alexander von Humboldt - Lima. Tema: La enseñanza de la matemática está en un proceso de cambio

Conceptos. ELO329: Diseño y Programación Orientados a Objetos. ELO 329: Diseño y Programación Orientados a Objetos

Ejercicio Nº 3: Realizar aumentos en una Tabla de Sueldos

Tema: Sobrecarga de Operadores.

MICROECONOMÍA II PRÁCTICA TEMA III: MONOPOLIO

Sistemas de numeración y aritmética binaria

Conclusiones. Particionado Consciente de los Datos

Profr. Efraín Soto Apolinar. Factorización

Este documento ha sido generado para facilitar la impresión de los contenidos. Los enlaces a otras páginas no serán funcionales.

Divisibilidad y números primos

Sistemas de numeración

Nano Taller de Python

Profr. Efraín Soto Apolinar. Números reales

8. Sentencia return y métodos

UNIDAD I NÚMEROS REALES

Matrices Invertibles y Elementos de Álgebra Matricial

Ámbito Científico-Tecnológico Módulo III Bloque 2 Unidad 1 Quien parte y reparte, se lleva la mejor parte

INSTITUTO VALLADOLID PREPARATORIA página 37

Lección 1-Introducción a los Polinomios y Suma y Resta de Polinomios. Dra. Noemí L. Ruiz Limardo 2009

Representación lógica del tiempo social

Operaciones en el Modelo Relacional. Relacional. Relacional. Índice. Lenguajes de Consulta

Nivel Básico I Prof. Mariano Correa

Módulo II - PowerPoint

Análisis de propuestas de evaluación en las aulas de América Latina

Compiladores e Intérpretes Proyecto N 1 Sintaxis de MiniJava Segundo Cuatrimestre de 2015

Tema 3. Polinomios y fracciones algebraicas

MATEMÁTICAS para estudiantes de primer curso de facultades y escuelas técnicas

VECTORES. Módulo, dirección y sentido de un vector fijo En un vector fijo se llama módulo del mismo a la longitud del segmento que lo define.

Matemáticas Propedéutico para Bachillerato. Introducción

x

Tema 2 Límites de Funciones

PRÁCTICAS DE GESTIÓN GANADERA:

TEMA 4 FRACCIONES MATEMÁTICAS 1º ESO

El número de arriba de la fracción, el numerador, nos dice cuántas de las partes iguales están coloreadas.

MATERIAL 2 EXCEL 2007

Descomposición factorial de polinomios

Práctica 2: Simón dice

Llamamos potencia a todo producto de factores iguales. Por ejemplo: 3 4 =

JORNADA DE TRABAJO LIQUIDACION

MODELOS DE RECUPERACION

El proceso de edición digital en Artelope y CTCE

SISTEMAS DE NUMERACIÓN. Sistema de numeración decimal: = =8245,97

Fundamentos de la Programación

Relaciones entre conjuntos

1. DML. Las subconsultas

Centro de Capacitación en Informática

TIPOS DE VARIABLES EN PHP. DECLARACIÓN Y ASIGNACIÓN. LA INSTRUCCIÓN ECHO PARA INSERTAR TEXTO O CÓDIGO. (CU00816B)

PROGRAMACIÓN ORIENTADA A OBJETOS

Teóricas de Análisis Matemático (28) - Práctica 4 - Límite de funciones. 1. Límites en el infinito - Asíntotas horizontales

ARBOLES ARBOLES BINARIOS ORDENADOS. REPRESENTACIÓN Y OPERACIONES

Transcripción:

Programación Funcional en Haskell Paradigmas de Lenguajes de Programación 1 cuatrimestre 2006 1. Expresiones, valores y tipos Un programa en lenguaje funcional consiste en definir expresiones que computan (o denotan) valores. Así como los valores, en el mundo real o matemático, pertenecen a un conjunto, las expresiones pertenecen a un tipo. Veamos qué tipos pueden tener las expresiones de Haskell: Tipos básicos como Int, Char, Bool, etc. Funciones, como a Int, Bool (Bool Bool), etc. Tuplas de cualquier longitud. Por ejemplo, (2 5 +1, 4 >0) es de tipo (Int, Bool). Listas, secuencias ordenadas de elementos de un mismo tipo, con repeticiones. [Int] representa el tipo lista de enteros, [Bool] es una lista de booleanos, etc. Las expresiones de tipo lista se construyen con [] (que representa la lista vacía) y : (a:as es la lista que empieza con el elemento a y sigue con la lista as). También pueden escribirse entre corchetes, con los elementos separados por comas: [] :: [Bool] [3] :: [Int] a : ( b : ( c : [])) :: [Char] [2 > 0, False, a == b ] :: [Bool] [[], [1], [1,2]] :: [[Int]] El tipo String es sinónimo de [Char], y las listas de este tipo se pueden escribir entre comillas: "plp" es lo mismo que [ p, l, p ]. Tipos definidos por el usuario, con la cláusula data. Los valores asociados a estos tipos consisten de un constructor (que se escribe con mayúscula) acompañado de 0 o más argumentos. data Dia = Lunes Martes Miercoles Jueves Viernes 1

Este tipo tiene cinco constructores, todos sin argumentos. A esta clase de tipos se los llama enumerados. data Either a b = Left a Right b data Maybe a = Nothing Just a Los tipos pueden tener argumentos, lo que los convierte en tipos paramétricos. Tipos como los de arriba suelen llamarse sumas o uniones, porque pueden representar la unión de varios tipos. En particular, Either representa la unión de dos tipos cualesquiera, y Maybe representa el mismo conjunto que su argumento, más un valor: Nothing. Left True :: Either True a Just 3 :: Maybe Int data BinTree a = Nil Branch a (BinTree a) (BinTree a) Acá vemos que algunos de los constructores pueden tener como argumento el mismo tipo que determinan. Tipos así se suelen llamar tipos recursivos. En este caso, BinTree a representa el tipo de los árboles binarios cuyos nodos tienen un elemento de a. Nil :: BinTree a Branch True Nil (Branch (4 > 0) Nil Nil) :: BinTree Int Las funciones sobre tipos construidos con la cláusula data pueden definirse por pattern matching. Un patrón consiste de un constructor con tantas variables como argumentos tenga; al evaluar la función en un argumento, se intenta establecer una correspondencia entre él y cada patrón, reduciendo en la primera ecuación donde se la encuentre. proximo :: Dia Dia proximo Lunes = Martes proximo Martes = Miercoles proximo Miercoles = Jueves etc. aint :: (Either Bool Int) Int aint (Left x) = if x then 1 else 0 aint (Right x) = x esvacio :: BinTree a b Bool esvacio Nil = True esvacio (Branch _) = False Cuando las variables no se usan en el lado derecho de la ecuación, se pueden reemplazar por un _. 2

Los tipos que permiten acceder a sus constructores y hacer pattern matching se llaman tipos algebraicos. Los booleanos, las tuplas y las listas también son tipos algebraicos! fst :: (a, b) a fst (x, y) = x length :: [a] Int length [] = [] length (x:xs) = 1 + length xs 2. Currificación y evaluación parcial Currificación es una correspondencia entre: funciones que reciben múltiples argumentos y devuelven un resultado suma :: (Int, Int) Int suma (x, y) = x + y funciones que reciben un argumento y devuelven una función intermedia que completa el trabajo suma :: Int Int Int suma x y = x + y En este ejemplo, suma x es una función que dado y devuelve x+y. Esta correspondencia siempre existe, y en el segundo caso decimos que las funciones están currificadas. La ventaja de las funciones currificadas es que permiten la aplicación parcial. En una sola línea estamos definiendo varias funciones! sucesor :: Int Int sucesor = suma 1 3. Polimorfismo y overloading El sistema de tipos de Haskell permite definir funciones para ser usadas con más de un tipo. Ya vimos algunos ejemplos: esvacio, fst y length son funciones polimórficas. Otras funciones polimórficas útiles son: flip :: (a b c) (b a c) flip f x y = f y x (.) :: (a b) (c a) (c b) (.) f g x = f (g x) 3

Las funciones polimórficas en general se definen según la estructura de sus argumentos, sin fijarse en qué valores tienen internamente. Por ejemplo, la longitud de una lista puede calcularse sin saber nada acerca de sus elementos. Veamos ahora este otro ejemplo. ejemplo 1: Definamos una función que devuelva verdadero cuando todos los elementos de una lista son iguales: todosiguales [] = True todosiguales [x] = True todosiguales (x:y:xs) = (x == y) && todosiguales (y:xs) Qué tipo tiene esta función? En principio, vemos que puede tomar listas de distintos tipos: todosiguales [1,2,3], todosiguales [True, True], todosiguales "hola" parecen expresiones válidas. Sin embargo, por ejemplo, todosiguales [sucesor, suma 1] no se podría evaluar, porque las funciones no pueden compararse por igualdad. Lo que necesitamos es describir el conjunto de tipos que tienen la operación ==, o más en general, los tipos que tienen ciertas operaciones en particular. Para ello, Haskell provee las clases de tipos. En este caso, los que pueden compararse por igualdad corresponden a la clase Eq. todosiguales :: Eq a [a] Bool Otras clases útiles son: Show: la clase de los tipos que pueden mostrarse por pantalla Ord: la clase de los tipos que pueden compararse (por menor, igual, etc.) Num: la clase de los tipos con operaciones aritméticas. El mecanismo de clases se denomina overloading. Notemos que == no es una función polimórfica, por más que pueda tomar argumentos de distintos tipos. Una función polimórfica tiene la misma definición para cualquier tipo, y como dijimos, no podrá explotar características particulares de cada uno. En cambio, una función sobrecargada, entre los distintos tipos, sólo comparte el nombre (y la aridad): su definición puede ser distinta para cada uno de ellos. 4. Alto orden En Haskell, las funciones son valores como cualquier otro: Pueden ser argumentos de una función Pueden ser resultados de otras funciones 4

Pueden almacenarse en estructuras de datos ejemplo 2: Definamos una función que toma el máximo de una lista: maximo :: Ord a [a] a maximo [x] = x maximo (x:y:xs) = if x > y then maximo (x:xs) else maximo (y:xs) Esta función es útil siempre y cuando no nos interese otro orden que el del operador >. maximo [1,4,3] = 4 maximo ["abc", "a", "b"] = "b" maximo [False, True] = True ejemplo 3: Ahora supongamos que quiero elegir, entre varias secuencias, la de mayor longitud. maxlongitud :: [[a]] [a] maxlongitud [xs] = xs maxlongitud (xs:ys:xss) = if length xs > length ys then maxlongitud (xs:xss) else maxlongitud (ys:xss) Esta función se parece mucho a la primera, y sin embargo, tuvimos que definirla aparte. Podremos generalizar maximo para que nos sirva en ambos casos? Sí: en lugar de tener (>) embebido en la definición de la función, tomemos una función de comparación como primer argumento! ejemplo 4: mejorsegun :: (a a Bool) [a] a mejorsegun _ [x] = x mejorsegun comp (x:y:xs) = if comp x y then mejorsegun comp (x:xs) else mejorsegun comp (y:xs) maximo = mejorsegun (>) maxlongitud = mejorsegun (λxs ys length xs > length ys) Y podemos definir más: minimo :: Ord a [a] a minimo = mejorsegun (<) maxelemento :: Ord a [[a]] [a] maxelemento = mejorsegun tienemaxelemento where tienemaxelemento xs ys = maximo xs > maximo ys 5

En este ejemplo mostramos varias formas de escribir funciones como argumentos de otras: Por su nombre, cuando la función está definida aparte: length Por sección de operadores: (>), ( 2), etc. Como funciones anónimas: (λxs ys length xs >length ys) Con cláusulas where: where tienemaximoelemento xs ys =maximo xs >maximo ys 5. Listas Las listas son una construcción muy útil en Haskell. Cuando un programa involucra una secuencia de valores, las listas suelen ayudar a expresarlo de una forma simple y clara. Hasta ahora vimos cómo escribir listas a partir de sus constructores, o de darlas explícitamente. Acá vamos a ver otras formas útiles de hacerlo. 5.1. Algunas funciones útiles sobre listas take n xs devuelve los n primeros elementos de xs drop n xs devuelve el resultado de sacarle a xs los primeros n elementos head xs devuelve el primer elemento de la lista tail xs devuelve toda la lista menos el primer elemento last xs devuelve el último elemento de la lista init xs devuelve toda la lista menos el último elemento xs ++ys concatena ambas listas xs!! n devuelve el n-ésimo elemento de xs elem x xs dice si x es un elemento de xs 5.2. Secuencias aritméticas Las siguientes expresiones representan listas de números en progresión aritmética: [1..4] = [1,2,3,4] [5,7..13] = [5,7,9,11,13] [1..] [2,4..] 6

De estas, las dos últimas representan listas infinitas. Como tales, por supuesto no tienen un valor asociado, pero pueden usarse para definir otras expresiones 1 : take 10 [1..] = [1,2,3,4,5,6,7,8,9,10] Claramente las secuencias aritméticas no son el único mecanismo para definir listas infinitas: infinitosunos :: [Int] infinitosunos = 1 : infinitosunos ejemplo 5: Cómo computar el factorial de un número? factorial :: Int Int factorial 0 = 1 factorial n = n factorial (n-1) factorial n = if n == 0 then 1 else n factorial (n-1) factorial n = product [1..n] Como vemos, el uso de listas nos da un código más sencillo y nos ahorra la necesidad de escribir la recursión explícitamente. 5.3. Listas por comprensión Las listas definidas por comprensión tienen la forma [expresion selectores, condiciones] donde un selector es de la forma var lista y una condición es una expresión booleana. Tanto la expresión como las condiciones pueden depender de las variables de los selectores. [(x,y) x [1,2], y [4,5]] = [(1,4),(1,5),(2,4),(2,5)] [(x,y) x [1,3], y [1..x]] = [(1,1), (2,1), (2,2), (3,1), (3,2), (3,3)] [(x,y) x [1,2], y [1..3], y > x] = [(1,2), (1,3), (2,3)] 1 Esto funciona bien porque Haskell utiliza evaluación lazy, que está emparentada con el orden normal de reducción: cuando una expresión puede, como la de arriba, reducirse de más de una forma, se elige la expresión más externa. En el ejemplo presentado, se podía reducir take 10 [1..] o solamente [1..], y esto último no hubiera terminado. Intuitivamente, la estrategia lazy evalúa los argumentos de las funciones sólo en la medida que es necesario. Entonces, en este caso, de la lista [1..] sólo hace falta computar los primeros diez elementos. La estrategia de evaluación eager, en cambio, está asociada al orden de reducción estricto: ante más de una opción, se reducen las expresiones más internas, con lo cual, los argumentos de las funciones se evalúan completamente antes de computarlas. 7

ejemplo 6: Usando listas por comprensión, podemos ordenar una lista con el algoritmo quicksort de una manera clara y concisa: quicksort [] = [] quicksort (x:xs) = quicksort [y y xs, y x] ++ [x] ++ quicksort [y y xs, y > x] ejemplo 7: Para decidir si un número es primo, en lugar de contar sus divisores con recursión explícita, basta con tomar la longitud de una lista: esprimo n = length [x x [1..n], n rem x == 0] == 2 6. Esquemas de funciones 6.1. Para listas ejemplo 8: Definamos una función que duplique los elementos de una lista de enteros. duplicar :: [Int] [Int] duplicar [] = [] duplicar (x:xs) = 2 x : duplicar xs duplicar xs = [2 x x xs] Definamos también una función que, dada una lista de cadenas, devuelva una lista con sus longitudes. longitudes :: [[a]] [Int] longitudes [] = [] longitudes (xs:xss) = length xs : longitudes xss longitudes xss = [length xs xs xss] Claramente estos esquemas son muy parecidos: lo único que cambia entre uno y otro es la función aplicada en el paso recursivo. Entonces, como ya hemos hecho, podemos generalizarlos en una función de alto orden: map :: (a b) [a] [b] map f [] = [] map f (x:xs) = f x : map f xs map f xs = [f x x xs] duplicar = map ( 2) longitudes = map length 8

ejemplo 9: Definamos una función que, dada una lista de enteros, devuelva los que son pares: pares :: [Int] [Int] pares [] = [] pares (x:xs) = if (rem x 2 == 0) then x : pares xs else pares xs pares xs = [x x xs, rem x 2 == 0] Y ahora otra que, dada una lista de cadenas y un número, devuelva una con las de mayor longitud que ese número: maslargasque :: Int [[a]] [[a]] maslargasque _ [] = [] maslargasque n (xs:xss) = if (length xs > n) then xs : maslargasque n xss else maslargasque n xss maslargasque n xs = [x x xs, length x > n] La única diferencia entre ellas es el primer argumento de if! Cómo podemos generalizarlas? filter :: (a Bool) [a] [a] filter _ [] = [] filter p (x:xs) = if p x then x : filter p xs else filter p xs filter p xs = [x x xs, p x] pares = filter (λx rem x 2 == 0) = filter ((== 0). ( rem 2)) = filter ((== 0). (flip rem 2)) maslargasque n = filter ((> n). length) ejemplo 10: Definamos ahora funciones para sumar los elementos de una lista, para multiplicarlos, para contarlos y para concatenarlos. sum :: Num a [a] a sum [] = 0 sum (x:xs) = x + sum xs product :: Num a [a] a product [] = 1 product (x:xs) = x product xs 9

length :: [a] Int length [] = 0 length (x:xs) = 1 + length xs concat :: [[a]] [a] concat [] = [] concat (xs:xss) = xs ++ concat xss Nuevamente tenemos un esquema que se repite en las tres funciones. En este caso, las diferencias están en el valor devuelto en el caso base y en la función aplicada en el caso recursivo. Así que vamos a abstraerlas para crear un esquema general. foldr :: (a b b) b [a] b foldr f z [] = z foldr f z (x:xs) = f x (foldr f z xs) sum = foldr (+) 0 product = foldr ( ) 1 length = foldr (λx n 1 + n) 0 concat = foldr (++) [] El esquema foldr sirve para recorrer una lista de derecha a izquierda : foldr op b (a1 : (a2 : (a3 : []))) = a1 op (a2 op (a3 op b )) Notemos acá como : se reemplaza por op y [] por b. ejemplo 11: Qué computan las siguientes funciones? f1 :: [Bool] Bool f1 = foldr (&&) True f2 :: [a] [a] f2 = foldr (:) [] f3 :: [a] [a] [a] f3 xs ys = foldr (:) ys xs Así como con foldr se asocia a derecha, podemos escribir un operador genérico de recursión que asocie a izquierda. foldl :: (b a b) b [a] b foldl f b [] = b foldl f b (x:xs) = foldl f (f b x) xs sum = foldl (+) 0 sum (a1 : (a2 : (a3 : []))) = foldl (+) (0 + a1) (a2 : (a3 : [])) = 10

foldl (+) ((0 + a1) + a2) (a3 : []) = foldl (+) (((0 + a1) + a2) + a3) [] = ((0 + a1) + a2) + a3 ejemplo 12: Qué computa las siguente función? f4 :: [a] [a] f4 = foldl (flip (:)) [] Cuando el caso base está en una lista unitaria en lugar de en una vacía, se pueden usar foldr1 y foldl1. foldr1 :: (a a a) [a] a foldr1 f (x:xs) = foldr f x xs foldl1 :: (a a a) [a] a foldl1 f (x:xs) = foldl f x xs maximo = foldr1 max Estos esquemas de recursión asocian a las listas un recorrido estándar, a partir del cual se puede definir un conjunto importante de operaciones. Todas ellas se pueden definir entonces sin pattern matching, concentrándonos únicamente en el aspecto de cada una que las diferencia de las demás. ejemplo 13: Definamos map y filter usando foldr map :: (a b) [a] [b] map f = foldr fun [] where fun x xs = f x : xs map f = foldr (λx xs f x : xs) filter :: (a Bool) [a] [a] filter p = foldr selec [] where selec x xs = if p x then x : xs else xs 6.2. Para otros tipos algebraicos Los esquemas generales de recursión pueden escribirse para cualquier tipo, y son muy útiles para evitar la repetición de código por pattern matching. En general, necesitamos: Para cada constructor base A a1... an del tipo, una función base z :: a1... an b. Para cada constructor recursivo, una función que tome, además de los argumentos no recursivos, los resultados acumulados, y devuelva un nuevo resultado acumulado. 11

Recordemos la definición de BinTree al principio: data BinTree a = Nil Branch a (BinTree a) (BinTree a) ejemplo 14: Empecemos por definir una función sobre BinTree Int, que multiplique los nodos del árbol, y otra que cuente los elementos: prodtree :: BinTree Int Int prodtree Nil = 1 prodtree (Branch x t1 t2) = x prodtree t1 prodtree t2 counttree :: BinTree a Int counttree Nil = 0 counttree (Branch x t1 t2) = 1 + counttree t1 + counttree t2 Acá, al igual que en las listas, hay un único caso base sin argumentos. Pero a diferencia de ellas, el caso recursivo tiene tres, dos de los cuales se corresponden con llamados recursivos propiamente dichos. Para definir foldtree, necesitaremos entonces una función f de tres argumentos: foldtree :: (a b b b) b BinTree a b foldtree f z Nil = z foldtree f z (Branch x t1 t2) = f x (foldtree f z t1) (foldtree f z t2) prodtree = foldtree (λx y z x y z) 1 counttree = foldtree (λx y z 1 + y + z) 0 ejemplo 15: Cómo podemos definir la función que dado un árbol, devuelva su simétrico? simetrico :: BinTree a BinTree a simetrico = foldtree rev Nil where rev x t1 t2 = Branch x t2 t1 Referencias [1] Página de Haskell www.haskell.org [2] A tour of the Haskell Prelude, describe y da ejemplos de las funciones de uso más común http://www.cs.uu.nl/%7eafie/haskell/tourofprelude.html [3] Haskell report es la especificación completa y oficial del lenguaje. http://www.haskell.org/onlinereport 12

[4] A tour of the Haskell Syntax, una descripción más amigable de la sintaxis de Haskell. http://www.cs.uu.nl/%7eafie/haskell/tourofsyntax.html [5] A gentel introduction to Haskell, uno de los tutoriales más famosos y bien completo. Incluye más temas que los que vamos a ver en la materia. http://www.haskell.org/tutorial [6] John Hughes, Why functional programming matters, Institutionen för Datavetenskap, Chalmers Tekniska Högskola. Disponible en: http://www.cs.chalmers.se/ rjmh/papers/whyfp.html [7] Graham Hutton, A tutorial on the universality and expressiveness of fold, University of Nottingham, UK. Disponible en: http://www.cs.nott.ac.uk/ gmh/fold.ps 13