Hacé cada ejercicio (tarjetas de crédito, tipos y hanoi) en un módulo Haskell distinto.
¿Alguna vez te preguntaste como hacían los sitios web para validar tu tarjeta de crédito cuando comprás en línea? No buscan en una base de datos gigantesca de números, tampoco usan magia. En realidad, la mayoría de los proveedores de tarjetas de crédito usan una fórmula de suma de control para distinguir números válidos de números aleatorios (o de errores de tipeo).
En esta sección, vas a implementar el algoritmo de validación para tarjetas de crédito. El algoritmo sigue los pasos siguientes:
[1,3,8,6]
se vuelve [2,3,16,6]
.[2,3,16,6]
se vuelve 2+3+1+6+6 = 18
.*
.0
entonces el número es válido.Primero necesitamos descomponer un número en su último dígito y el resto del número. Escribí las funciones siguientes:
lastDigit :: Integer -> Integer
dropLastDigit :: Integer -> Integer
Si estás perdido, buscá los operadores aritméticos que vimos más temprano en esta materia:
Ejemplos:
123 == 3
lastDigit 0 == 0
lastDigit 123 == 12
dropLastDigit 5 == 0 dropLastDigit
Ahora podemos descomponer un número en sus dígitos. Definí la función
toDigits :: Integer -> [Integer]
toDigits
debe convertir enteros positivos en una lista de dígitos. Para 0 o valores negativos, debe devolver la lista vacía.
Ejemplos:
1234 == [1,2,3,4]
toDigits 0 == []
toDigits -17) == [] toDigits (
Una vez que tenemos los dígitos en el orden correcto, necesitamos duplicar uno de cada dos. Definí la función:
doubleEveryOther :: [Integer] -> [Integer]
Recordá que doubleEveryOther
debería duplicar cada otro número empezando desde la derecha.
Es mucho más fácil llevar a cabo esta operación sobre una lista de dígitos que está en orden inverso. Probablemente vas a necesitar definir unas funciones auxiliares para que eso ande.
Ejemplos:
8,7,6,5] == [16,7,12,5]
doubleEveryOther [1,2,3] == [1,4,3] doubleEveryOther [
La salida de doubleEveryOther
es una mezcla de números de uno y dos dígitos. Definí la función
sumDigits :: [Integer] -> Integer
para calcular la suma de todos los dígitos.
Ejemplo:
16,7,12,5] = 1 + 6 + 7 + 1 + 2 + 5 = 22 sumDigits [
Definí la función
validate :: Integer -> Bool
que indica si un entero es un número válido de tarjeta de crédito.
Usará todas las funciones definidas en las partes anteriores:
Ejemplos:
4012888888881881 == True
validate 4012888888881882 == False validate
Definir los tipos y funciones siguientes en un archivo tipos.hs
.
Cuando los distintos valores que debemos distinguir en un tipo son finitos, podemos enumerar todos los valores distintos para el tipo. Por ejemplo, podrı́amos representar los tı́tulos nobiliarios de algún paı́s (retrógrado) con el siguiente tipo:
data Titulo = Ducado | Marquesado | Condado | Vizcondado | Baronia
Titulo
como descrito arriba. No tiene que pertenecer a la clase Eq
.hombre :: Titulo -> String
que devuelve la forma masculina del tı́tulo.dama
que devuelve la inflexión femenina.En este ejercicio, introducimos dos conceptos: los sinónimos de tipos y tipos algebraicos cuyos constructores llevan parámetros. Los sinónimos de tipos nos permiten definir nombres de tipos nuevo a partir de tipos ya existentes:
-- Territorio y Nombre son sinonimos de tipo.
type Territorio = String
type Nombre = String
bsas :: Territorio
cba ,= "Cordoba"
cba = "Buenos Aires"
bsas
bob :: Nombre
alicia ,= "Alicia"
alicia = "Bob" bob
Los tipos algebraicos tienen constructores que llevan parámetros. Esos parámetros nos permiten agregar información; por ejemplo, en este ejercicio además de distinguir el rango de cada persona, representamos datos pertinentes a cada tipo de persona:
-- Persona es un tipo algebraico
data Persona = Rey -- constructor sin parametro
| Noble Titulo Territorio -- constructor con dos parametros
| Caballero Nombre -- constructor con un parametro
| Aldeano Nombre -- constructor con un parametro
Territorio
, Nombre
y Persona
como descrito arriba. Este último tipo no debe pertenecer a la clase Eq
.tratamiento :: Persona -> String
que dada una persona devuelve la forma en que se lo menciona en la corte. Esto es, al rey se lo nombra “Su majestad el rey”, a un noble se lo nombra “El data Genero = Hombre | Mujer
a nuestro módulo. ¿Cómo modificar el tipo Persona
para poder representar personas de distintos géneros sin agregarle más constructores? Realizá esta modificación y vuelva a programar la función tratamiento
de forma tal de respetar el género de la persona al nombrarla.sirs :: [Persona] -> [String]
que dada un lista de personas devuelve solo los nombres de los hombres. Definila con dos casos: el caso base y el inductivo.sirs'
, con un solo caso, usando la función filter
.Maybe
Maybe
es un tipo predefinido en Haskell cuya definición es la siguiente:
data Maybe a = Nothing | Just a
El tipo Maybe
sirve para “envolver” un tipo existente (el tipo a
) y darle un valor extra (Nothing
) que representa la ausencia de valor, o alguna falla.
El tipo Maybe
es polimórfico porque en situaciones concretas, puede tomar como parámetro otro tipo: Maybe Int
, Maybe String
, etc.
Definí las funciones siguientes:
division_segura :: Int -> Int -> Maybe Int
, que toma dos enteros y devuelve Nothing
si el segundo es cero, o Just c
donde c
es el cociente del primero por el segundo.cabeza :: [a] -> Maybe a
que toma una lista y devuelve el primer elemento (dentro de Just
) solamente si la lista no es vacía, y Nothing
si es vacía.suma_maybe :: Maybe Int -> Maybe Int -> Maybe Int
que toma dos “enteros Maybe”, y solo devuelve un Just
de la suma si los dos son Just
. En los otros casos, devuelve Nothing
.suma_maybe_num :: Num a => Maybe a -> Maybe a -> Maybe a
, una versión más general de suma_maybe
.Consideremos la siguiente definición de tipo de datos:
data ListAssoc a b = Empty
| Node a b (ListAssoc a b)
Este tipo es recursivo porque él mismo (ListAssoc a b
) aparece como un argumento de uno de sus constructores (Node
). Los parámetross del constructor de tipo (ListAssoc
) indican que es un tipo polimórfico, donde las variables a
y b
se pueden instanciar con distintos tipos; por ejemplo:
type Diccionario = ListAssoc String String
type DNI = Int
type Padron = ListAssoc DNI String
Definí las siguientes funciones:
la_comodin :: Int -> ListAssoc Int Int
que, dada un entero positivo n
, construye un lista asociativa de n
entradas desde n
hasta 1
, cada entrada teniendo asociado el valor 0. Por ejemplo:
1 == Node 1 0 Empty
la_comodin 3 == Node 3 0 (Node 2 0 (Node 1 0 Empty))
la_comodin 0 == Empty la_comodin
Como ListAssoc
no pertenece a la clase Show
, no vas a poder comprobar esta función sola en GHCi, recién en conjunto con las funciones siguientes vas a poder ver si anda bien.
la_long :: ListAssoc a b -> Int
que devuelve la cantidad de datos en una lista.
la_pares :: ListAssoc a b -> [(a,b)]
que devuelve la lista de pares contenida en la lista de asociaciones.
la_existe :: Eq a => ListAssoc a b -> a -> Bool
que dada una lista y una clave devuelve True
si la clave está y False
en caso contrario.
la_buscar :: Eq a => ListAssoc a b -> a -> Maybe b
que dada una lista y una clave devuelve el dato asociado si es que existe. Podés consultar la definición del tipo Maybe
en Hoogle.
la_agregar :: Eq a => a -> b -> ListAssoc a b -> ListAssoc a b
que dada una clave a
, un dato b
lo agrega en la lista si la clave a NO existe. En caso de que la clave a
exista, se aplasta el dato anterior.
la_borrar :: Eq a => a ListAssoc a b -> ListAssoc a b
que dada una clave a
elimina la entrada en la lista.