1 algoritmos y estructuras de datos i programación funcional clase 3
TRANSCRIPT
1
Algoritmos y Estructuras de Datos I
Programación funcional
Clase 3
2
Tipos recursivosEl tipo definido es argumento de alguno de los constructores.Ejemplos :
data N = Z | S N
El tipo N tiene por supuesto al elemento ZConstructor 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 principioPor ahora solamente conocemos un N que es ZPodemos formar únicamente el elemento S ZEs un nuevo NPodemos 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
Tipos recursivosdata BE = TT | FF | AA BE BE | NN BE
Dos constructores recursivosUno 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 FFAA 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 AANN(NN(AA TT NN(TT)))
Por último, términos que contengan AA como argumentos de AAAA (AA(NN FF)(TT)) (AA(AA(NN TT) TT) FF)
Este tipo también podría representar uno más conocido, ¿Cuál?
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
Ejemplossize :: N -> Int
size Z = 0 size (S x) = 1 + size x
addN :: N -> N -> N addN Z m = maddN (S n) m = S (addN n m)
contarAes :: BE -> Int contarAes FF = 0contarAes TT = 0contarAes (NN b) = contarAes bcontarAes (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
Listas
Es el tipo recursivo más usadoBrinda 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úndata List a = Nil | Cons a (List a)
Después vamos a ver la notación de Haskell, que es más cómoda.
7
List a
data List a = Nil | Cons a (List a)
Tipo paramétrico.Instanciémoslo: List IntTenemos Nil (sin parámetros) Lo interpretamos como secuencia vacía
Podemos usar ConsPor ahora, Cons n NilListas unitarias Cons 2 Nil
Y seguimos con ConsCons 2 (Cons 3 (Cons 2 (Cons 0 Nil)))
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
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
Haciendo recursión estructural (List a)
sum :: [Int] -> Int sum [ ] = 0sum (n:ns) = n + sum ns
length :: [a] -> Int length [ ] = 0length (x:xs) = 1 + length xs
(++) :: [a] -> [a] -> [a][ ] ++ ys = ys(x:xs) ++ ys = x : (xs ++ ys)
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
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íaEjemplo de importancia de contar con una especificación precisa.
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
Un árbol
Nodo 1
Hoja 0
Nodo 3
Hoja 2
Hoja 0
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
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
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
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
Ó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
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
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
Motivación para tipos abstractosVimos 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 apagarloObjetos 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 quierePero también alentarlo a hacerlo bien. Y, más importante, impedirle hacerlo mal:
usar la representación interna del tipoen otros programas
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 funcionesMensaje de error si se intenta violar ese acuerdo.
24
Mantenimiento de programas
Acceso por pattern matching limitado a pocas funcionesSi decidimos cambiar la representación interna, el cambio se va a limitar también a esas funcionesEn lo posible, muy cerca una de otra en el código (mismo archivo).
Si no pedimos al lenguaje esta protecciónRiesgo de usar (nosotros mismos u otro programador) la implementación del tipo en muchos otros programasCuando 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
Uso de tipos abstractos
Recibimos un tipo de datos abstractonombre del tiponombres de sus operaciones básicastipos de las operacionesespecificación de su funcionamiento
Puede ser más o menos formalEn Haskell no es chequeada por el lenguaje
¿Cómo se utiliza el tipo?A través de sus operacionesY únicamente asíNo solo por convicción: no tenemos otro remedio
26
Ejemplo: racionales
Nos dan un tipo de datos abstracto que representa los racionalesNo 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 -> RacionalnumerR :: Racional -> IntdenomR :: 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
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
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 fallasEn esta materia no van a aprender cómo comunicarse con dispositivos así desde Haskell. Como programadores-usuarios no nos interesa.
29
Operaciones del diccionario
Buscar:: Diccionario -> Palabra -> Maybe Definicionagregar::(Palabra,Definicion)-> Diccionario-> Diccionarioeliminar:: Palabra -> Diccionario -> Diccionariocastellano:: Diccionario
Transparencia referencialagregar y eliminar no modifican, no cambian un diccionarioCrean un diccionario nuevo a partir de uno existente y unos datos más.
agregar (“eldiego", “jugador de futbol") castellano Construye diccionario nuevoPuedo preguntarle qué quiere decir “eldiego” castellano sigue siendo el mismo diccionario¿Cómo se llama el nuevo diccionario?
No tiene un nombreLa forma de hablar de él es con la expresión enteraSi queremos hacer la pregunta:
buscar(agregar(“eldiego",“jugador de futbol") castellano) “eldiego"
Devuelve Just “jugador de futbol"
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
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
Operaciones de conjuntos
El conjunto vacíoempty :: 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 igualinsert :: Int -> IntSet -> IntSet
Elegir el menor número y quitarlo del conjuntochoose :: IntSet -> (Int, IntSet)
33
Transparencia referencial
Las operaciones no modifican los conjuntos, construyen conjuntos nuevosPero es fácil explicar las cosas en estos términos
choose no quita nadaEl original, sigue igual que antesDevuelve un entero y otro conjunto, con todos los elementos del primero menos eseNo queda claro qué pasa si el conjunto es vacíoSe puede suponer que da error
34
Nuevas operaciones sobre conjuntos
Como programadores-usuarios, definamos operación nueva: uniónunion :: IntSet -> IntSet -> IntSet
union p q | isEmpty p = qunion 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 matchingunion está definida en forma recursiva (a través de unionAux)Pero no usa recursión estructural ¡ni siquiera conocemos la estructura!
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
EncapsulamientoAgrupar estructura de datos con funciones básicasPrograma 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 exportaCuáles van a poder usarse desde afuera y cuáles noAplicación: ocultar funciones auxiliares (como unionAux)También para crear tipos de datos abstractos debemos ocultar la representación interna
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) wheredata Complejo = C Float FloatparteReal, parteIm:: Complejo -> FloatparteReal(C r i) = rparteIm (C r i) = i
module introduce el módulo: nombre, qué exportaNombre (..) exporta el nombre del tipo y sus constructoresUn programador que lo use puede hacer pattern matchingDespués, where y definicionesSi no se pone la lista de exportación (Module Complejos where...), se exportan todos los nombres definidos
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
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
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 importEs 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 whereimport Complejosimport Racionales (Racional, crearR)miPar :: (Complejo, Racional)miPar = (C 1 0, crearR 4 2)
Si se pone algunas de estas expresiones: va a dar errornumerR (snd miPar)reduce (crearR 4 2)R 4 2 + R 2 1
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 = Stringtype Sueldo = Inttype Empleado = (Nombre, Sueldo)type Direccion = Stringtype Persona = (Nombre, Direccion)Persona es par String, pero si usamos (String, String), es difícil
entenderTambié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
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
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, peroLlama la atención sobre renombre
A otro implementador encargado de modificarla,A alguien que tenga que analizarAl mismo implementador dentro de un tiempo
Admite un solo constructor con un parámetrono crea nuevos elementosrenombra elementos existentes (no intercambiable)
Mejor rendimiento que data
43
Implementación de conjuntosRepresentación: listas ordenadas sin elementos repetidosmodule ConjuntoInt
(IntSet, empty, isEmpty, belongs, insert, choose) where
import qualified List (insert)newtype IntSet = Set [Int]empty :: IntSetempty = Set []isEmpty :: IntSet -> BoolisEmpty (Set xs) = null xs
belongs :: Int -> IntSet -> Boolbelongs x (Set xs) = x ‘elem‘ xs
insert :: Int -> IntSet -> IntSetinsert x (Set xs) | x ‘elem‘ xs = Set xsinsert x (Set xs) | otherwise = Set (List.insert x xs)
choose :: IntSet -> (Int, IntSet)choose (Set (x:xs)) = (x, Set xs)
44
Aclaraciones
Función insert del modulo List Es útil para implementar los conjuntosInserta un elemento en su lugar correspondiente de una lista ordenadaPero 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 ListPara referirme a ese nombre tengo que calificarlo con el del módulo. Tengo que escribir List.insertEsto 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
FinTerminamos 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
47
Polimorfismo
Dijimos que el tipado en Haskell es fuertePero es común querer escribir funciones que puedan usarse, sin
redefinirlas, para distintos tipos de datosSe llama polimorfismoEl comportamiento de la función no depende del valor de sus parámetrosEjemplo: id (identidad): id x = x
¿De qué tipo es id?id 3, entonces id :: Int -> Intid True, entonces id :: Bool -> Boolid doble, entonces id :: (Int -> Int) -> (Int -> Int)
Respuesta: id :: a -> a No importa qué tipo sea aUsamos variables de tipoid es un tipo parámetrico (depende de un parámetro) Es una función polimórficaSe lee “id es una función que dado un elemento de algún tipo a devuelve otro
elemento de ese mismo tipo.”
48
Currificación
promedio1 :: (Float,Float) -> Floatpromedio1 (x,y) = (x+y)/2
promedio2 :: Float -> Float -> Floatpromedio2 x y = (x+y)/2
promedio1 recibe como único argumento un par ordenadopromedio2 recibe dos argumentos de tipo Float
Separamos los tipos de los argumentos por una flechaIgual a la que separa los tipos de los argumentos del tipo del resultadoNo es caprichoso, tiene implicancias teóricas y prácticas (no vamos a verlas)
La notación (una operación que permite) se llama currificaciónPor Haskell B. Curry
Matemático estadounidenseDe 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
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
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.