1 1 Algoritmos y Estructuras de Datos I Programación funcional Clase 3
2 2 Tipos recursivos El tipo definido es argumento de alguno de los constructores.Ejemplos : data N = Z | S N El tipo N tiene por supuesto al elemento Z Constructor sin argumentos (como False en Bool, Calor en Sensacion) El constructor S Fabrica un N a partir de otro N, agreg á ndole una S al principio Por ahora solamente conocemos un N que es Z Podemos formar ú nicamente el elemento S Z Es un nuevo N Podemos usarlo con el constructor S para fabricar otro N : S(S Z) Y as í sucesivamente Entonces, elementos del tipo N son Z, S Z, S(S Z), S(S(S Z)), S(S(....(S Z))...)
3 3 Tipos recursivos data BE = TT | FF | AA BE BE | NN BE Dos constructores recursivos Uno con dos par á metros del mismo tipo Tenemos dos elementos: TT y FF (constructores sin par á metros) Podemos usarlos con NN para formar valores parecidos a los de N NN(NN(...(NN TT))...) NN(NN(...(NN FF))...) O podemos combinarlos con AA AA TT FF AA FF FF O usar los BE que construimos con NN como argumentos de AA AA (NN(NN TT)) FF Tambi é n combinaci ó n inversa: aplicar NN a t é rminos que comienzan con AA NN(NN(AA TT NN(TT))) Por ú ltimo, t é rminos que contengan AA como argumentos de AA AA (AA(NN FF)(TT)) (AA(AA(NN TT) TT) FF) Este tipo tambi é n podr í a representar uno m á s conocido, ¿ Cu á l?
4 4 Usando recursión estructural Da una explosión combinatoria de valores posibles. Pero también, una forma de controlarla: Los dominios tienen muchos términos. Todos tienen que estar creados con uno de los constructores aplicados a cero o más argumentos. Usando pattern matching, podemos definir funciones recursivas sobre cualquier término mediante recursión estructural.
5 5 Ejemplos size :: N -> Int size Z = 0 size (S x) = 1 + size x addN :: N -> N -> N addN Z m = m addN (S n) m = S (addN n m) contarAes :: BE -> Int contarAes FF = 0 contarAes TT = 0 contarAes (NN b) = contarAes b contarAes (AA b1 b2) = 2 + contarAes b1 + contarAes b2 Para poder aplicar la funci ó n a todas las expresiones del tipo, tenemos que poner por lo menos una ecuaci ó n para cada constructor Para que el paso recursivo sea correcto, hay que usar, del lado derecho del igual, subexpresiones de la que aparece del izquierdo.
6 6 Listas Es el tipo recursivo más usado Brinda un tratamiento de secuencias. Hay muchas funciones para listas en preludio Son tipos algebraicos con sintaxis propia Primero, vamos a definirlas como un tipo algebraico común data List a = Nil | Cons a (List a) Después vamos a ver la notación de Haskell, que es más cómoda.
7 7 List a data List a = Nil | Cons a (List a) Tipo paramétrico. Instanciémoslo: List Int Tenemos Nil (sin parámetros) Lo interpretamos como secuencia vacía Podemos usar Cons Por ahora, Cons n Nil Listas unitarias Cons 2 Nil Y seguimos con Cons Cons 2 (Cons 3 (Cons 2 (Cons 0 Nil)))
8 8 Haciendo recursión estructural ( List ) Sumemos todos los elementos de una lista de enteros sumList :: List Int -> Int sumList Nil = 0 sumList (Cons n ns) = n + sumList ns
9 9 Sintaxis Haskell para listas (notación más cómoda) Nil se escribe [] (Cons x xs) se escribe (x:xs) Dos contructores : [] Cons 2 (Cons 3 (Cons 2 (Cons 0 Nil))) se escribe 2 : 3 : 2 : 0 : [] Notación más cómoda [2,3,2,0]
10 10 Haciendo recursión estructural (List a) sum :: [Int] -> Int sum [ ] = 0 sum (n:ns) = n + sum ns length :: [a] -> Int length [ ] = 0 length (x:xs) = 1 + length xs (++) :: [a] -> [a] -> [a] [ ] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)
11 11 Operadores Se llama operador a una funci ó n cuyo nombre empieza con un car á cter especial. Si nos referimos a la funci ó n sin aplicarla o en forma prefija, se usa entre par é ntesis. Sino la usamos en forma infija entre sus operandos. Ejemplo: La función de concatenación de listas (++) Ejemplo: [1, 2] ++ [3, 4, 5]
12 12 Transparencia referencial “Quiero cambiar el primer elemento de una lista de naturales por un cuatro.” No se puede hacer, los objetos no cambian de valor: listaUno :: [Int] listaUno = [1, 2, 3, 4] “Dada una lista, quiero obtener otra que coincida con ella en todas las posiciones, excepto en la primera, que debe ser un cuatro.” Matemática: si dos listas difieren en un elemento, no son la misma ponerCuatro :: [Int] -> [Int] ponerCuatro (x : xs) = (4 : xs) No cambia ningún elemento de la lista. Crea una lista nueva parecida a la primera. Repetimos xs en el resultado para que estuviera el resto. Si aplicamos a listaUno, obtenemos [4, 2, 3, 4] El valor de listaUno va a seguir siendo el mismo Faltaría ecuación para explicar lista vacía! Que dice la especificación? Cambiar un elemento por otro. Alternativa a) lo dejamos así. (Si aplica a la lista vacía, da error: no hay elemento a cambiar por 4). Alternativa b) Nueva especificación para devolver [4] Alternativa c) devolver la lista vacía Ejemplo de importancia de contar con una especificación precisa.
13 13 Árboles Estructura formada por nodos que almacenan valores de manera jerárquica. Vamos a trabajar con árboles binarios: Cada nodo almacena un valor, y de cada nodo salen, o bien dos nodos, o bien ningun nodo. Se llama Hoja a aquel nodo del que no sale ningun otro nodo. Se llama Rama a aquel nodo del que salen otros dos nodos. Se llama Raíz a aquel nodo que no tiene padre. Típicamente los árboles se dibujan en forma invertida: Raíz arriba de todo y las Hojas abajo.
14 14 Un árbol Nodo 1 Hoja 0 Nodo 3 Hoja 2 Hoja 0
15 15 Tipo de datos Arbol a Definición del tipo data Arbol a = Hoja a | Nodo a (Arbol a) (Arbol a) El árbol del ejemplo aej :: Arbol Int aej = Nodo 1 (Hoja 0) (Nodo 3 (Hoja 2) (Hoja 0)) Funciones (recursión estructural) hojas :: Arbol a -> Int hojas (Hoja x) = 1 hojas (Nodo x i d) = hojas i + hojas d altura :: Arbol a -> Int altura (Hoja x) = 0 altura (Nodo x i d) = 1 +(altura i ‘max‘ altura d)
16 16 Transparencia referencial Funci ó n parecida a ponerCuatro. Se puede decir que cambia los n ú meros 2 de las hojas por n ú meros 3 Pero sabemos que fabrica á rboles nuevos, con los mismos datos que el recibido como argumento, excepto algunos cambiar2 :: Arbol Int -> Arbol Int cambiar2 (Hoja n) | n==2 = Hoja 3 cambiar2 (Hoja n) | otherwise = Hoja n cambiar2 (Nodo n i d) = Nodo n (cambiar2 i) (cambiar2 d)
17 17 Ejercicio: Reducir cambiar2 aej Ejercicio: Reducir duplA aej y sumA aej duplA :: Arbol Int -> Arbol Int duplA (Hoja n) = Hoja (n*2) duplA (Nodo n i d) = Nodo (n*2) (duplA i) (duplA d) sumA :: Arbol Int -> Int sumA (Hoja n) = n sumA (Nodo n i d) = n + sumA i + sumA d.
18 18 Recorriendo un árbol Las funciones que vimos van “recorriendo” un árbol y operando con cada nodo. Podrían hacerlo en cualquier orden con el mismo resultado final. Sin embargo, a veces es importante el orden al recorrer un árbol para aplicarle alguna operación. Por ejemplo, buscamos en qué ubicación está la primera aparición de un dato en un árbol. La interpretación de “primera” puede cambiar. Vamos a ver dos funciones que arman una lista con los elementos de un árbol. Ambas respetan el orden de los elementos en el árbol, pero el criterio para respetar ese orden es distinto
19 19 Órdenes de recorrido de árboles 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) Ejercicio: Reducir inOrder aej y preOrder aej. Ejercicio: Definir un tercer orden: posOrder.
20 20 Aplicaciones con Conversión de tipos Convertimos el árbol en lista, de acuerdo a algún orden. Podemos aprovechar funciones sobre listas para realizar operaciones sobre los elementos del árbol. Por ejemplo podemos redefinir sumA: sumA a = sum (preOrder a)
21 21 Utilidad de Polimorfismos La mayoría de estas funciones son polimórficas (son casos más interesantes que la identidad). Estas funciones (como preOrder) se basan solamente la estructura de sus argumentos: No importa su valor. Son aplicables a muchos tipos de datos, tanto existentes como por existir.
22 22 Motivación para tipos abstractos Vimos cómo crear y cómo usar tipos de datos algebraicos. Al usar un tipo, se lo puede tratar como un tipo abstracto: Recibimos (o entregamos) un tipo de datos sin permitir acceso a representación No hace falta más de un programador. Puedo crear un tipo (como un tipo algebraico) que voy a usar yo mismo y nadie más, pero ocultar su implementación. En otras partes del programa me está prohibido accederla ¿Para qué puedo querer ponerme límites yo mismo? Despertador lejos de la cama para evitar el reflejo de apagarlo Objetos frente a la puerta de salida para no irse sin ellos En el diseño de lenguajes de programación es un punto clave. Los lenguajes deben facilitar que el programador haga lo que quiere Pero también alentarlo a hacerlo bien. Y, más importante, impedirle hacerlo mal: usar la representación interna del tipo en otros programas
23 23 Recordemos los requisitos para tipos algebraicos data Racional = R Int Int Se puede usar, pero con cuidado: Nunca construir con segunda componente 0. Funciones: mismo resultado para todas las representaciones del mismo racional Función numerador que devuelve la primera componente Resultados distintos para R (-1) (-2) y R 2 4 Son el mismo número, no estamos representando las fracciones El lenguaje puede ayudar: Recursos sintácticos para avisar que esta representación interna se usa en unas pocas funciones Mensaje de error si se intenta violar ese acuerdo.
24 24 Mantenimiento de programas Acceso por pattern matching limitado a pocas funciones Si decidimos cambiar la representación interna, el cambio se va a limitar también a esas funciones En lo posible, muy cerca una de otra en el código (mismo archivo). Si no pedimos al lenguaje esta protección Riesgo de usar (nosotros mismos u otro programador) la implementación del tipo en muchos otros programas Cuando la reemplacemos, van a dejar de funcionar todos esos programas Hasta que los modifiquemos uno por uno. Es bueno que el lenguaje brinde una manera de abstraernos de la representaci ó n interna de los tipos. Vamos a ver: C ó mo usar los tipos abstractos que recibimos ya implementados. C ó mo hacer para presentar como abstractos los tipos que creamos.
25 25 Uso de tipos abstractos Recibimos un tipo de datos abstracto nombre del tipo nombres de sus operaciones b á sicas tipos de las operaciones especificaci ó n de su funcionamiento Puede ser m á s o menos formal En Haskell no es chequeada por el lenguaje ¿ C ó mo se utiliza el tipo? A trav é s de sus operaciones Y ú nicamente as í No solo por convicci ó n: no tenemos otro remedio
26 26 Ejemplo: racionales Nos dan un tipo de datos abstracto que representa los racionales No nos importa c ó mo fue definido. Quiz á s, con un tipo algebraico como vimos antes, pero no podemos aplicar pattern matching. Recibimos operaciones: crearR :: Int -> Int -> Racional numerR :: Racional -> Int denomR :: Racional -> Int Especificaci ó n Numerador y denominador: forma normalizada (m á xima simplificaci ó n) con signo en el numerador. No sabemos cu á ndo se produce la simplificaci ó n para normalizar al construir el n ú mero en crearR al evaluarlo, en numerR y denomR numerR (crearR (-1) (-2)) == numerR (crearR 2 4) Tambi é n aclara que crearR n 0 da error
27 27 Operaciones sobre racionales Tal vez nos den operaciones b á sicas (suma y multiplicaci ó n) Tambi é n podr í amos 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)
28 28 Otro ejemplo: diccionario Guarda definiciones para ciertas palabras (como diccionario de la lengua) Visto en forma genérica (paramétrico), se usa mucho en los programas de computación. Por ejemplo, en lugar de relacionar palabras con definiciones, relaciona números de teléfonos con abonadosCombinado con un dispositivo de identificación de llamadas: averiguar el nombre de quien me llama. Almacenamiento: Para el implementador, ambos diccionarios (palabras y teléfonos) estarían fuera de la memoria Medio externo con más capacidad y tolerancia a fallas En esta materia no van a aprender cómo comunicarse con dispositivos así desde Haskell. Como programadores-usuarios no nos interesa.
29 29 Operaciones del diccionario Buscar:: Diccionario -> Palabra -> Maybe Definicion agregar::(Palabra,Definicion)-> Diccionario-> Diccionario eliminar:: Palabra -> Diccionario -> Diccionario castellano:: Diccionario Transparencia referencial agregar y eliminar no modifican, no cambian un diccionario Crean un diccionario nuevo a partir de uno existente y unos datos m á s. agregar (“eldiego", “jugador de futbol") castellano Construye diccionario nuevo Puedo preguntarle qu é quiere decir “ eldiego ” castellano sigue siendo el mismo diccionario ¿ C ó mo se llama el nuevo diccionario? No tiene un nombre La forma de hablar de é l es con la expresi ó n entera Si queremos hacer la pregunta: buscar(agregar(“eldiego",“jugador de futbol") castellano) “eldiego" Devuelve Just “jugador de futbol"
30 30 Sobre el diccionario Adem á s de las operaciones y sus tipos, el dise ñ ador nos da una especificaci ó n Agregar dos veces una palabra a un diccionario es lo mismo que agregarla una vez. No importa en qu é orden se agreguen dos palabras. Si se saca una palabra, su definici ó n desaparece.
31 31 Otro ejemplo: conjuntos El tipo de datos preferido para representar colecciones en programación funcional no es conjuntos, sino listas, porque los conjuntos no pueden representarse con un tipo algebraico que cumpla las condiciones que pusimos (los constructores permiten armar de varias formas el mismo conjunto, o expresiones que no son conjuntos). La solución es crear un tipo abstracto para los conjuntos. Las operaciones disponibles para el programador-usuario van a limitar su uso como pasó con los racionales
32 32 Operaciones de conjuntos El conjunto vac í o empty :: IntSet ¿ El conjunto dado es vac í o? isEmpty :: IntSet -> Bool ¿ Un elemento pertenece al conjunto? belongs :: Int -> IntSet -> Bool Agregar un elemento al conjunto, si no estaba; si estaba, dejarlo igual insert :: Int -> IntSet -> IntSet Elegir el menor n ú mero y quitarlo del conjunto choose :: IntSet -> (Int, IntSet)
33 33 Transparencia referencial Las operaciones no modifican los conjuntos, construyen conjuntos nuevos Pero es f á cil explicar las cosas en estos t é rminos choose no quita nada El original, sigue igual que antes Devuelve un entero y otro conjunto, con todos los elementos del primero menos ese No queda claro qu é pasa si el conjunto es vac í o Se puede suponer que da error
34 34 Nuevas operaciones sobre conjuntos Como programadores-usuarios, definamos operaci ó n nueva: uni ó n union :: IntSet -> IntSet -> IntSet union p q | isEmpty p = q union p q | otherwise = unionAux (choose p) q unionAux (x,p') q = insert x (union p' q) Pudimos hacerlo sin conocer la representaci ó n de los conjuntos. Usamos las operaciones provistas. Si cambia la representaci ó n el implementador tiene que rescribir las ecuaciones para las operaciones del tipo abstracto ( empty, isEmpty, belongs, insert, choose ) union y cualquier otra definida en otros programas quedan intactas. Observemos que la recursi ó n no es privativa del pattern matching union est á definida en forma recursiva (a trav é s de unionAux ) Pero no usa recursi ó n estructural ¡ ni siquiera conocemos la estructura!
35 35 Creación de tipos abstractos Pasemos al rol de implementadores. Escribir un tipo abstracto que puedan usar otros programadores. En Haskell se hace con módulos. Son archivos de texto que contienen parte de un programa. Por ejemplo: uno o más tipos de datos con sus funciones. O, simplemente, un grupo de funciones. Dos características importantes en cualquier lenguaje Encapsulamiento Agrupar estructura de datos con funciones básicas Programa no es lista de definiciones y tipos, son “cápsulas” Cada una con un (tal vez más) tipo de datos y sus funciones específicas Ocultamiento Cuando escribo un módulo indico qué nombres exporta Cuáles van a poder usarse desde afuera y cuáles no Aplicación: ocultar funciones auxiliares (como unionAux) También para crear tipos de datos abstractos debemos ocultar la representación interna
36 36 Ejemplo de módulo El primer ejemplo no es un tipo abstracto, sino algebraico. Vamos a agrupar la funcionalidad de los complejos y exportar el tipo completamente 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 module introduce el m ó dulo: nombre, qu é exporta Nombre (..) exporta el nombre del tipo y sus constructores Un programador que lo use puede hacer pattern matching Despu é s, where y definiciones Si no se pone la lista de exportaci ó n ( Module Complejos where... ), se exportan todos los nombres definidos
37 37 Ejemplo de módulo de tipo abstracto module Racionales (Racional, crearR, numerR, denomR) where data Racional = R Int Int crearR :: Int -> Int -> Racional crearR n d = reduce (n*signum d) (abs d) reduce :: Int -> Int -> Racional reduce 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
38 38 Aclaraciones signum (signo), abs (valor absoluto), quot (divisi ó n entera) y gcd (gratest common divisor (MCD)) est á n en el preludio. En la lista de exportaci ó n no dice (..) despu é s de Racional Se exporta solamente el nombre del tipo NO sus constructores Convierte el tipo en abstracto para los programadores que lo usen Tampoco exportamos la funci ó n auxiliar reduce.
39 39 Ahora usemos el tipo Volvemos al rol de programador-usuario. Indicar (en otro m ó dulo) que se quiere incorporar este tipo de datos. Se usa la cl á usula import Es posible importar todos los nombres exportados por un m ó dulo (importando el nombre del m ó dulo) O solamente algunos de ellos (aclarando entre par é ntesis cu á les) module Main where import Complejos import Racionales (Racional, crearR) miPar :: (Complejo, Racional) miPar = (C 1 0, crearR 4 2) Si se pone algunas de estas expresiones: va a dar error numerR (snd miPar) reduce (crearR 4 2) R 4 2 + R 2 1
40 40 Sinónimos de tipo Nombre nuevo a un tipo de Haskell: se usa la cl á usula type. No se crea un nuevo tipo, sino un sin ó nimo de tipo. Los dos nombres son equivalentes. Ejemplo: nombrar una instancia particular de un tipo param é trico: type String = [Char] type IntOChar = Either Int Char O renombrar tipo existentes con nombre m á s significativo: type Nombre = String type Sueldo = Int type Empleado = (Nombre, Sueldo) type Direccion = String type Persona = (Nombre, Direccion) Persona es par String, pero si usamos (String, String), es dif í cil entender Tambi é n sin ó nimos de tipos param é tricos: type Lista a = [a] type IntY a = (Int, a) No es una forma de definir tipos, no admite recursi ó n
41 41 Otra forma de crear tipos Vimos c ó mo crear sin ó nimos de tipos (es un nombre adicional para un tipo existente, y resulta intercambiable) A veces queremos un tipo nuevo con la representaci ó n de uno existente, pero que NO puedan intercambiarse. Si la representaci ó n de un tipo abstracto es uno que ya existe, no usamos sin ó nimo: Un programador-usuario podr í a aprovecharlo y tomar elementos iguales como diferentes, crear valores inv á lidos, atar el programa a esta representaci ó n. Usamos la cl á usula newtype
42 42 newtype Ejemplo, conjuntos de enteros. Los representamos internamente con listas y encerramos la representaci ó n en un tipo abstracto [1, 3, 2, 4, 3] = [1, 2, 3, 4] newtype IntSet = Set [Int] Muy similar a data, pero Llama la atenci ó n sobre renombre A otro implementador encargado de modificarla, A alguien que tenga que analizar Al mismo implementador dentro de un tiempo Admite un solo constructor con un par á metro no crea nuevos elementos renombra elementos existentes (no intercambiable) Mejor rendimiento que data
43 43 Implementación de conjuntos Representaci ó n: listas ordenadas sin elementos repetidos module ConjuntoInt (IntSet, empty, isEmpty, belongs, insert, choose) where import qualified List (insert) newtype IntSet = Set [Int] empty :: IntSet empty = Set [] isEmpty :: IntSet -> Bool isEmpty (Set xs) = null xs belongs :: Int -> IntSet -> Bool belongs x (Set xs) = x ‘elem‘ xs insert :: Int -> IntSet -> IntSet insert x (Set xs) | x ‘elem‘ xs = Set xs insert x (Set xs) | otherwise = Set (List.insert x xs) choose :: IntSet -> (Int, IntSet) choose (Set (x:xs)) = (x, Set xs)
44 44 Aclaraciones Funci ó n insert del modulo List Es ú til para implementar los conjuntos Inserta un elemento en su lugar correspondiente de una lista ordenada Pero estaba definiendo una funci ó n insert para conjuntos, y no puedo tener en el mismo programa las dos funciones Us é qualified al importar insert del m ó dulo List Para referirme a ese nombre tengo que calificarlo con el del m ó dulo. Tengo que escribir List.insert Esto me permiti ó distinguir los dos insert Las funciones null (ver si una lista es vac í a) y elem (ver si un elemento pertenece a una lista) est á n en el preludio.
45 45 Fin Terminamos el tema de definici ó n de tipos en Haskell hasta el nivel con el que vamos a usarlos en la materia. Dejamos afuera: chiches de notaci ó n (como definir campos con nombre para los tipos algebraicos) un tema conceptualmente complejo: clases de tipos. Son extensi ó n sobre el sistema de tipos de Hindley-Milner y agrega mucho poder m á s all á del polimorfismo (overloading). Pero con las herramientas que les dimos van a poder crear programas interesantes como el del proyecto de taller.
46 46
47 47 Polimorfismo Dijimos que el tipado en Haskell es fuerte Pero es com ú n querer escribir funciones que puedan usarse, sin redefinirlas, para distintos tipos de datos Se llama polimorfismo El comportamiento de la funci ó n no depende del valor de sus par á metros Ejemplo: id (identidad): id x = x ¿ De qu é tipo es id? id 3, entonces id :: Int -> Int id True, entonces id :: Bool -> Bool id doble, entonces id :: (Int -> Int) -> (Int -> Int) Respuesta: id :: a -> a No importa qu é tipo sea a Usamos variables de tipo id es un tipo par á metrico (depende de un par á metro) Es una funci ó n polim ó rfica Se lee “ id es una funci ó n que dado un elemento de alg ú n tipo a devuelve otro elemento de ese mismo tipo. ”
48 48 Currificación promedio1 :: (Float,Float) -> Float promedio1 (x,y) = (x+y)/2 promedio2 :: Float -> Float -> Float promedio2 x y = (x+y)/2 promedio1 recibe como ú nico argumento un par ordenado promedio2 recibe dos argumentos de tipo Float Separamos los tipos de los argumentos por una flecha Igual a la que separa los tipos de los argumentos del tipo del resultado No es caprichoso, tiene implicancias te ó ricas y pr á cticas (no vamos a verlas) La notaci ó n (una operaci ó n que permite) se llama currificaci ó n Por Haskell B. Curry Matem á tico estadounidense De su nombre tambi é n se tom ó el del lenguaje que est á n aprendiendo Alcanza con ver que evita varios signos de puntuaci ó n (par é ntesis y comas): promedio1 (promedio1 (2, 3), promedio1 (1, 2)) promedio2 (promedio2 2 3) (promedio2 1 2)
49 49 Roles Personas que trabajan en un desarrollo con tipos abstractos –Una persona puede ocupar m á s de uno En cada momento debe tener claro en cu á l est á –Un rol puede ser ejercido por un equipo Dise ñ ador –Establece operaciones –Escribe las especificaciones –Puede decidir o sugerir representaci ó n interna Implementador –Escribe el c ó digo para definir el tipo, representaci ó n y funciones b á sicas QU É hacer (decisiones del dise ñ ador) C Ó MO hacerlo (c ó digo escrito por el implementador) Programador-usuario –Utiliza las operaciones No sabe c ó mo est á n implementadas O sabe, pero no puede aprovecharlo –Se desentiende de la implementaci ó n y usa el tipo como primitivo –Ya usamos tipos abstractos ( Int, Float )
50 50 Rol de los estudiantes en Algo I Pr á cticas y parciales –Pogramadores-usuarios Usar tipos de datos que se les van a proveer Taller –Adem á s, implementadores –Implementar tipos de datos abstractos –Ahora veremos c ó mo Dise ñ adores –Siempre los docentes de la materia –Escapa al alcance de la misma –Incluso, en algunos casos, al del á rea de Programaci ó n.