Sie sind auf Seite 1von 10

Programación funcional – Haskell

Tipos de datos simples


Booleanos
El tipo de datos Bool comprende valores que se corresponden con la declaración de tipos de datos:
data Bool = False | True deriving Show
esta declaración denomina Bool al tipo de datos, y True y False a sus 2 valores constituyentes, que
extienden la clase Show que permite su visualización en pantalla.
Una vez introducido Bool se pueden definir funciones que tomen argumentos booleanos, por
ejemplo la función primitiva negación:
not :: Bool -> Bool
not False = True
not True = False
El lenguaje usa estas 2 ecuaciones como reglas de reescritura para simplificar expresiones de la
forma not e. Primeramente, e se reduce a forma normal. Si este proceso termina y devuelve False,
entonces se utiliza la primera ecuación; si la reducción devuelve True, entonces se usa la segunda
ecuación. Si no se puede reducir e a forma normal, entonces el valor de not e es indefinido.
Dos funciones básicas sobre los booleanos son las operaciones de conjunción, denotada por el
operador binario &&, y disyunción, denotada por ||. Estas operaciones predefinidas admitirían la
definición:
(&&),( || ) :: Bool -> Bool -> Bool
False && x = False
True && x = x
(*Defina de manera análoga la operación disyunción*) 1
Hay 2 operadores de igualdad, == y /=. Se definen para argumentos booleanos de este modo:
(= =), (/=) :: Bool -> Bool -> Bool
x = = y = (x && y) || (not x && not y)
(*Defina /= basándose en = =*) 2
Ejercicios:
1. Defina la función bisiesto que determine si un año es bisiesto o no (un año es bisiesto si es divisible por
4, excepto si es divisible por 100, en cuyo caso debe ser también divisible por 400). 3
bisiesto :: Integer -> Bool

bisiesto x = ……………………………………………………………………………….
2. La siguiente función triángulo toma 3 enteros positivos x, y, z (en orden no decreciente) que
representan las longitudes de los lados de un posible triángulo (3 lados forman un triángulo si y
sólo si la longitud del lado más largo es menor que la suma de las longitudes de los otros 2
lados), y determina si los 3 lados constituyen un triángulo y, de ser así, si el triángulo es escaleno
(3 lados diferentes), isósceles (2 lados iguales) o equilátero (los 3 lados iguales).
Así triangulo debe devolver 1 de 4 valores distintos. Se puede entonces definir:
data Triangulo = Fallo | Isósceles | Equilátero | Escaleno
triangulo :: (Integer, Integer, Integer) -> Triangulo
triangulo (x, y, z)
| x + y <= z = Fallo
|x== z = Equilátero
| (x = = y) || (y = = z) = Isósceles
| otherwise = Escaleno
(*Defina una función Ordena3 que ordene 3 enteros en orden creciente. A partir
de ella definir una función triangulo que no dependa de la suposición de que sus
argumentos están en orden no decreciente*) 4

Caracteres
El tipo de datos predefinido Char contiene 256 caracteres, incluyendo tanto caracteres visibles como
de control. Los caracteres se denotan encerrándolos entre símbolos de comillas simples. Son
expresiones en forma canónica, por lo tanto el evaluador simplemente los muestra, incluyendo las
comillas.
Se proporcionan 2 funciones primitivas, ord y chr, para procesar caracteres. Sus tipos son:
ord :: Char -> Int
chr :: Int -> Char
la función ord convierte un carácter c a un entero que lo representa en el rango 0 <= ord c < 255, y
la función chr hace lo contrario, convertir un entero al carácter que representa.
Se pueden definir funciones simples sobre caracteres, por ejemplo las siguientes que determinan si
un carácter es un dígito, una letra minúscula o una letra minúscula:
isDigit, isLower, isUpper :: Char -> Bool
isDigit c = (‘0’ ≤ c) ^ (c ≤ ‘9’)
(*Defina de manera análoga la función isLower o isUpper*) 5
Ejercicios:

1
Programación funcional – Haskell
3. Defina una función aMayúscula que convierta minúsculas en mayúsculas. 6
4. Definir una función siguienteLetra que tome como argumento una letra del alfabeto y devuelva
la letra que le sigue. Suponer que la letra ‘A’ sigue a la ‘Z’. 7
5. Definir una función valorNum que convierta un carácter dígito en su correspondiente valor
numérico. 8

Enumeraciones
Un modo de definir un nuevo tipo de datos es enumerar explícitamente sus valores. Por ejemplo:
data Dia = Dom | Lun | Mar | Mie | Jue | Vie | Sab deriving (Eq, Ord, Enum, Show)
Las 7 constantes Dom, Lun, y así sucesivamente se denominan constructoras del tipo de datos Dia.
Los elementos del tipo Dia se pueden comparar, siendo conveniente para ello codificarlos como
enteros, y usar comparaciones entre enteros. Esto sucede con otros tipos enumerados, que se
consideran instancias de la clase de tipos Enum que describe tipos cuyos elementos pueden ser
enumerados. La declaración implícita es:
class Enum a where
toEnum :: Integer -> a
fromEnum :: a -> Integer
Se debe definir: toEnum :: Integer -> Dia
toEnum x |x==Dom = 0
|x==Lun = 1
|x==Mar = 2
|x==Mie = 3
|x==Jue = 4
|x==Vie = 5
|x==Sab = 6
En relación al tipo Char visto anteriormente, se podría declarar:
instance Enum Char where
toEnum = ord
fromEnum = chr
Los operadores de igualdad y desigualdad son sobrecargados, definiéndose de modo diferente para
distintos tipos de datos y, por lo tanto, está declarada primero una clase de tipos Eq que consta de
todos aquellos tipos para los cuales = = y ≠ están definidos: class Eq α where
(= =), (≠ ) :: α → α → Bool
Asimismo las operaciones de comparación están sobrecargadas, estando declarada otra clase de
tipos Ord de este modo: class (Eq α) ⇒ Ord α where
(<),(≤ ),(≥ ),(>) :: α -> α -> Bool
(x ≤ y) = (x < y) ∨(x = = y)
(x ≥ y) = (x > y) ∨(x = = y)
(x > y) = not (x ≤ y)
La primera línea dice que Ord es una subclase de Eq, esto es que sólo puede dotarse con un orden a
los tipos con igualdad. Las 3 últimas líneas dan definiciones por defecto de las operaciones (≤ ), (≥ )
y (>). Estas definiciones por defecto ahorran el trabajo de tener que repetirlas en cada declaración de
concreción. Por lo tanto es suficiente con dar la definición de (<) para cada concreción.
Dada la función toEnum de Dia, podemos declarar:
instance Eq Dia where
(x = = y) = (toEnum x = = toEnum y)
instance Ord Dia where
(x < y) = (toEnum x < toEnum y)
Ejercicios:
6. Usando toEnum y fromEnum definir una función diaDespues que devuelva el día siguiente a un día dado. 9
7. Definir un tipo de datos Direccion cuyos valores describan los puntos cardinales de la brújula y definir una función
invertir para invertir una dirección. 10

2
Programación funcional – Haskell
Listas
El tipo de datos de las listas es la espina dorsal de la programación funcional. Las listas se pueden
usar para transportar datos de una función a otra; las listas de números se pueden sumar y
multiplicar; las listas de caracteres se pueden leer e imprimir; y así sucesivamente.
Una lista finita se denota empleando corchetes y comas. Por ejemplo, [1, 2, 3] es una lista de 3
números y [“hola”, “adiós”] es una lista de 2 cadenas. Todos los elementos de una lista han de ser
del mismo tipo. La lista vacía se escribe [ ] y la lista unitaria, que contiene un solo elemento x, se
escribe [x]. En particular, [[]] es una lista unitaria cuyo único elemento es la lista vacía.
Si todos los elementos de una lista tienen tipo α, se le asigna a la lista el tipo [α]. Por ejemplo:
[1, 2, 3] :: [Integer]
[‘h’, ‘o’, ‘l’, ‘a’] :: [Char]
[[1, 2], [3]] :: [[Integer]]
[(+), (*)] :: [Integer → Integer → Integer]
Las cadenas son simplemente listas de caracteres escritas con una sintaxis especial. Así, “hola” es
tan sólo una sintaxis adicional conveniente para la lista [‘h’, ‘o’, ‘l’, ‘a’]. Toda operación genérica
sobre listas es, por tanto, aplicable también a las cadenas.
A diferencia de los conjuntos, una lista puede contener el mismo valor más de una vez. Por ejemplo:
[1, 1].
Se puede construir una lista desde cero, comenzando con la lista vacía y añadiendo sucesivamente
elementos uno por uno. Se puede añadir elementos al principio de la lista o al final, o en algún punto
intermedio.
En la programación funcional, las listas son elementos de algún tipo α, y se usa la sintaxis [α] para
designar las listas del tipo α, la notación [] para la lista vacía, y la construcción de listas se efectúa con
el operador infijo ( : ). Por lo tanto: [1, 2, 3] = 1 : (2 : (3 : [])) = 1 : 2 : 3 : []
En otras palabras, la sintaxis especial de la izquierda puede verse como una abreviatura de la
sintaxis de la derecha.
Podemos convenir en denotar elementos de listas con letras x, y, z, etc. Y a las propias listas
mediante xs, ys, zs, etc.
La función predefinida null comprueba si una lista está vacía:
null :: [α] -> Bool
null [] = True
null (x : xs) = False
La función predefinida last devuelve el último elemento de una lista no vacía:
last (x : xs) = if null xs then x else last xs
Ejercicios:
8. Indicar la signatura de tipo para la función last. 11
9. Cómo definiría la función head que devuelve el primer elemento de una lista no vacía. 12
10. Escribir la función tail que devuelve el resto de la lista (excluyendo el primer elemento) 13
11. Escribir una función que genere de manera recursiva una lista conformada por los números del 1 a n. 14
12. Escriba los pasos de evaluación que se verifican cuando se evalúa con la lista [1, 2] la función
predefinida init , definida como se indica a continuación. ¿Qué acción realiza dicha función? 15
init :: forall a.[a] -> [a]
init (x : xs) = if null xs then [ ] else x : init xs
Operaciones sobre listas
Concatenación
Esta función se denota mediante el operador binario ++ (concatenar). Por ejemplo:
? [1, 2, 3] ++ [4, 5]
[1, 2, 3, 4, 5]
La definición formal de ++ es:
(++) :: [α] → [α] → [α]
[ ] ++ ys = ys
(x :xs) ++ ys = x : (xs ++ ys)
La concatenación toma 2 listas, ambas del mismo tipo, y produce una tercera lista, también del mismo tipo.
De aquí la signatura de tipos.
La segunda ecuación para ++ es muy sucinta y requiere alguna consideración. Si se comprende bien la
definición de ++, se ha comprendido gran parte de cómo funcionan las listas en la programación funcional.
Nótese que el número de pasos requeridos para calcular xs ++ ys es proporcional al número de elementos de
xs. Por ejemplo:
[1, 2] ++ [3, 4, 5]
= {notación}
(1: (2 : [])) ++ (3 : (4 : (5 : [ ])))

= {segunda ecuación de ++}


1 : ((2 : ([]) ++ (3 : (4 : (5 : [ ])))
= {segunda ecuación de ++}
1 : (2 : ([] ++ (3 : (4 : (5 : [ ]))))
= {primera ecuación de ++}
1 : (2 : (3 : (4 : (5 :[ ]))))
= {notación}

3
Programación funcional – Haskell
[1, 2, 3, 4, 5]
Inversión
La función predefinida reverse invierte el orden de los elementos de una lista finita. Por ejemplo:
? reverse “Dabale el abad.”
“.daba le elabaD”
(*Completar la definición de reverse :*) 16
reverse :: forall a.[a] -> …..
reverse [ ] =[]
reverse (x : xs) = ………….
Dicho en palabras, para invertir una lista (x : xs) se invierte xs y luego se añade x al final.
Longitud de una lista
La longitud de una lista es el número de elementos que contiene:
length :: forall a.[a] -> Integer
length [ ] = 0
length (x : xs) = 1 + length xs
Sublistas
Las funciones predefinidas head, tail, last e init, respectivamente, devuelven el primer elemento de una lista
no vacía, el resto de la lista tras quitar el primer elemento, el último elemento de una lista no vacía, y la lista
que queda tras eliminar el último elemento.
Las funciones predefinidas take y drop toman cada una un entero no negativo n y una lista xs como
argumentos. El valor de take n xs es la lista constituida por los primeros n elementos de la lista xs y el de
drop n xs es lo que queda de la lista. Por ejemplo:
? take 3 “funcional”
“fun”
? drop 3 “funcional”
“cional”
? take 3 [1, 2]
[1, 2]
? drop 3 [1, 2]
[]
(*Completar las definiciones :*) 17

take :: Integer → [α] → [α] drop :: Integer → [α] → [α]


take 0 xs =[] drop 0 xs = ..?..
take (n + 1) [ ] =[] drop (n + 1) [ ] = ..?..
take (n + 1) (x : xs) = ..?.. drop (n + 1) (x : xs) = drop n xs
Indexación de listas
Para hallar el elemento que ocupa la posición n, se puede indexar una lista mediante un número natural n.
Esta operación se denota xs !! n. Por ejemplo:
?[1, 2, 3, 4] !! 2
3
Esta operación es bastante costosa en la programación funcional, ya que necesita un número de pasos de
reducción proporcional a n.
Las funciones map y filter
La función map aplica una función a cada elemento de una lista. Por ejemplo:
? map (^2) [9, 3]
[81, 9]
? map (< 3) [1, 2, 3]
[True, True, False]
? map siguienteLetra “HAL”
“IBM”
Su definición es:
map :: (α -> β) -> [α] -> [β]
map f [ ] =[]
map f (x : xs) = f x : map f xs
La función filter toma una función booleana f y una lista xs y devuelve la sublista de xs cuyos elementos
satisfacen f. Por ejemplo:
? filter even [1, 2, 4, 5, 32]
[2, 4, 32]
? filter odd [1, 2, 4, 5, 32]
[1, 5]
? sum (map (^2) ( (filter even) [1..10] ))
220
En el último ejemplo, la notación [1 .. 10] es equivalente a invocar una función que devuelve la lista
conformada por los números comprendidos entre 1 y 10 inclusive.
Su definición es:
filter :: (α → Bool) → [α] → [α]
filter f [ ] =[]
filter f (x : xs) = if f x then x : filter f xs else filter f xs
Ejercicios:
13. Defina la función booleana even que dado un entero devuelve True si el mismo es par. 18
14. Defina la función numérica sum que devuelve la suma de los componentes de una lista numérica. 19
15. Definir la función upto que dados 2 enteros, devuelve la lista conformada por los números comprendidos entre
ambos inclusive. 20

4
Programación funcional – Haskell
Resumen del lenguaje
Funciones
nombre_función :: Tipo_fuente -> Tipo_resultado ⇒ descripciones (signaturas) de tipo
nombre_función (argumentos) = expresión ⇒ definición funcional (puede ser recursiva)
[where nombre_local = expresión] ⇒ definición local
nombre_función comienza con minúsculas, al igual que los nombres de variables correspondientes a argumentos. La parte de la definición “=
expresión”, constituye una ecuación.
Tipos
Bool (valores True o False), Int (valores numéricos enteros, con hasta 10 cifras decimales en el rango -2 31 a 231-1),
Integer (valores numéricos enteros de rango arbitrario), Float (hasta 7 dígitos decimales de precisión), Double (hasta 15
dígitos), Char (los valores constantes se encierran entre comillas simples), String (las constantes de este tipo se
encierran entre comillas dobles).
Tuplas: (tipo_valor1, tipo_valor2, …) ⇒ pueden ser de diferentes tipos, por ej. (50, “Alvarez”) es del tipo (Int, String)
Renombramientos: type Nombre = tipo ⇒ no define un tipo nuevo, sólo renombra alguno existente (puede dar nombre a
tuplas). Por ej: type Edad=Int type Nombre=String type Persona=(Edad, Nombre).
Enumerados: data Nombre = valor1|valor2|… deriving (Enum, Show). Ej: data Dia = Lun|Mar|Mié|Jue|Vie|Sáb|Dom deriving
(Enum,Show)
Listas: [tipo] ⇒ el tipo String corresponde a una lista de caracteres: [Char]
Los valores constantes numéricos negativos se indican encerrándolos entre paréntesis (e incluyendo el signo).
Expresiones
Condicional: if condición then expresión_verdad else expresión_falsedad
ecuaciones con guardas: |condición_1 = expresión_1
|condición_2 = expresión_2 …
[ |otherwise = expresión_n]
Operadores
Aritméticos: + - * / ^
div mod ⇒ deben usarse como funciones prefijas cuprificadas (por ej: mod 23 11 devuelve 1)
(^2) ⇒ secciones (operadores con un argumento que actúan como funciones prefijas currificadas)
Lógicos: == /= > >= < <= (de comparación)
not && ||
Strings / Listas: ++ (concatenación), !! (indexación, :: ([α ],Int)->α ), [m..n] (devuelve la lista con los valores entre m
y n inclusive)
Funciones predefinidas
Números: sqrt, abs, even (devuelve True si el argumento es par), odd (devuelve True si el argumento es impar), min
(función prefija currificada), max
Char: ord (::Char->Int)(devuelve el valor ASCII de un caracter), chr (::Int->Char)(devuelve el carácter correspondiente
a un valor ASCII), min, max
Tuplas: fst (:: (α ,β )->α ), snd (:: (α ,β )->β ) ⇒ actúan sobre pares
Enumeraciones: toEnum (::Int->α , devuelve el valor enumerado correspondiente al orden provisto), fromEnum (::α ->Int)
Listas: null (:: [α ]->Bool), head (:: [α ]->α , devuelve el primer elemento), last (:: [α ]->α , devuelve el último
elemento), reverse (:: [α ]->[α ], invierte la lista), length (:: [α ]->Int, devuelve la longitud de la lista), tail (::[α ]-
>[α ], devuelve lo que queda después de eliminar el primer elemento de una lista), init (::[α ]->[α ], devuelve lo que
queda después de eliminar el último elemento), take (:: (Int,[α ])->[α ], devuelve la lista formada por los primeros n
elementos de una lista xs), drop (:: (Int,[α ])->[α ], devuelve la lista formada por los elementos que quedan después de
eliminar los primeros n elementos de una lista xs), sum (suma los elementos de la lista), product (multiplica los
elementos de la lista), maximum (devuelve el máximo de la lista), minimum (devuelve el mínimo de la lista), map (::
(α ->β )->[α ]->[β ], aplica una función a cada elemento de una lista), filter (:: (α ->Bool)->[α ]->[α ], aplica una
función booleana f a una lista xs, y devuelve la sublista de xs cuyos elementos satisfacen f), zip (toma como argumentos
2 listas y devuelve la lista de los pares de elementos que se corresponden, hasta donde pueda hacer corresponder), unzip
(toma una lista de pares y la separa en dos listas)
Listas intensionales: notación alternativa para cálculos que necesitan map y filter. Por ejemplo: map (^2) (filter odd
[1..10]) en estilo combinatorio, se expresa de manera intensional: [x^2|x<-[1..10],odd x]
expresión generador guarda
cualificador
Un generador es de la forma x<-xs, donde x es una variable o tupla de variables y xs una lista. Una guarda es una
expresión booleana. Puede haber una secuencia de generadores y guardas, separados por comas. Por ejemplo, [(a,b)|a<-
[1..3],b<-[1..2]] daría como resultado [(1,1),(1,2),(2,1),(2,2),(3,1),(3,2)]. La lista [(a,b)|a<-[1..3],even a,b<-[1..2],odd
b] resultará en [(2,1)].
Ejemplos:
triplas :: Int -> [(Int, Int, Int) pitag :: (Int, Int, Int) -> Bool
triplas n = [(x, y, z) | x <- [1..n], y <- [x..n], z <- [y..n]] pitag (x, y, z) = (x^2 + y^2 = z^2)
devuelve las ternas distintas de valores entre 1 y n devuelve True si la terna es pitagórica

triadas :: Int -> [(Int, Int, Int)] devuelve las ternas pitagóricas de valores entre 1 y n
triadas n = [(x, y, z) | (x, y, z) <- triplas n, pitag (x, y, z)]

pe :: [Int]->[Int]->Int devuelve el producto escalar de los vectores


pe xs ys = sum(map mult(zip xs ys)) where mult (x,y) = x*y xs e ys

posiciones :: Int->[Int]->[Int] devuelve la lista de las posiciones en las que aparece el valor x
posiciones x xs = [i|(i,y)<-zip[0..] xs, x==y] dentro de la lista xs

posición :: Int -> [Int] -> Int devuelve la posición en que aparece por primera vez el valor x
posición x xs = head(posiciones x xs ++ [-1]) dentro de la lista xs, ó -1 si no aparece

5
Programación funcional – Haskell

Tipos: Maybe
Either
Ordering
Rational
Clases: Eq
Ord
Enum
Bounded
Num
Real
Fractional
Floating
RealFrac
RealFloat
Monad, Functor
Funciones: curry (tuplas)
uncurry ( “ )
maybe (Maybe)
either (Either)
succ, pred (Enum)
enumFrom, enumFromThen, enumFromTo, enumFromThenTo (Enum)
minBound, maxBound (Bounded)
negate, signum, fromInteger (Num)
toRational (Real)
recip, fromRational (Fractional)
pi, exp,log, (**), logBase, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, saín, acosh, atanh (Floating)
properFraction, truncate, round, ceiling, floor (RealFrac)
floatRadix, floatDigits, floatRange, decodeFloat, encodeFloat, exponent, significand (RealFloat)
scaleFloat, isNaN, isInfinite, isDenormalized, isIEEE, isNegativeZero, atan2 (RealFloat)
subtract, gcd, lcm, (^^), fromIntegral, realToFract (Num)
(>>=), (>>), return, fail, fmap, mapM, mapM_, sequence, sequence_, (=<<) (Monad/Functor)
id, const, (.), flip, ($), until, asTypeOf, error, undefined, seq, ($!) (miscellaneous)
foldl, foldl1, foldr, foldr1, and, or, any, all. Concat, concatMap, scanl, scanl1, scanr, scanr1 (List)
iterate, repeat, replicate, cycle, splitAt, takeWhile, dropWhile, span, break, elem, notElem, lookup (List)
zip3, zipWith, zipWith3, unzip3 (List)
lines, words, unlines, unwords (String)
ReadS, ShowS, Read, Show, reads, shows, read, lex, showChar, showString, readParen, showParen (String
convertion)
String: putStr (:: String->IO( ))
IO, putChar, putStr, puStrLn, print, getChar, getLine, getContents, interact
FilePath, readFile, writeFile, appendFile, readIO, readLn
IOError, ioError, userError, catch

6
Programación funcional – Haskell
Ejemplo: convertir números en palabras
A veces se necesita expresar números con letras, debiendo aparecer una cantidad no sólo en cifras
sino que también ha de ser escrita en letras, por ejemplo en un cheque.
Supongamos que el número es un entero mayor que cero pero menor que un millón, y se quiere
diseñar una función convertir de tal modo que el valor de convertir n sea la lista de caracteres que
corresponde a la expresión en castellano del número n.
Por ejemplo, deberá ser:
? convertir 369401
“trescientos sesenta y nueve mil cuatrocientos uno”
Una buena manera de tratar problemas como éste es considerar primero un problema más simple, ya
que a menudo la solución encontrada se podrá usar directamente o adaptar para el problema más
complejo.
Supongamos entonces que el número n pertenece a un intervalo más pequeño, por ejemplo 0 < n <
100. Se puede en este caso definir:
convertir2 :: Integer → String
convertir2 = combinar2 . digitos2

digitos2 :: Integer → (Integer, Integer)


digitos2 n = (n div 10, n mod 10)
Así será, por ejemplo: digitos2 9 = (0, 9) digitos2 99 = (9, 9)
Para definir combinar2 necesitamos nombres en castellano para los números más simples. Pueden
darse como listas de cadenas:
unidades, dieciAlgo, veintiAlgo, decenas :: [String]
unidades = [“uno”, “dos, “tres”, “cuatro”, “cinco”, “seis”, “siete”, “ocho”, “nueve”]
dieciAlgo = [“diez”, “once”, “doce”, trece”, “catorce”, “quince”, “dieciséis”, “diecisiete”, “dieciocho”, “diecinueve”]
veintiAlgo = [“veinte”, “veintiuno”, “veintidós”, “veintitres”, “veinticuatro”, “veinticinco”, “veintiséis”, “veintisiete”, “veintiocho”,
“veintinueve”]
decenas = [“veinte”, “treinta”, cuarenta”, “cincuenta”, “sesenta”, “setenta”, “ochenta”, “noventa”]
La definición de combinar2 emplea estas listas para extraer los elementos apropiados dependiendo
de los dígitos:
combinar2 :: (Integer, Integer) → String
combinar2 (0, u + 1) = unidades !! u
combinar2 (1, u) = dieciAlgo !! u
combinar2 (2, u) = veintiAlgo !! u
combinar2 (t + 3,0) = decenas !! t
combinar2 (t + 3, u + 1) = decenas !! t ++ “⊔y⊔” ++ unidades !! u
El operador !! permite tomar de una lista el elemento que ocupa la posición n-ésima, considerando la
primera posición como 0.
Completar la definición de !!: 21
(!!) :: [α] → Integer → …..
(x : xs) !! 0 = …..
(x : xs) !! (n + 1) = xs !! n
Ampliemos ahora el rango a 0 < n < 1000. Para ello definimos:
convertir3 :: Integer → String
convertir3 = combinar3 . digitos3

digitos3 :: Integer → (Integer, Integer)


digitos3 n = (n div 100, n mod 100)

cientos = [“ciento”, “doscientos”, “trescientos”, “cuatrocientos”, “quinientos”, “seiscientos”, “setecientos”,


“ochocientos”, “novecientos”]

combinar3 :: (Integer, Integer) → String


combinar3 (0, t + 1) = convertir2 (t + 1)
combinar3 (h + 1, 0) = cientos !! h
combinar3 (h + 1, t + 1) = cientos !! h ++ “⊔” ++ convertir2 (t + 1)
Este paso es el más importante en el diseño del algoritmo completo. Descomponemos n en dígitos
en dos etapas: primero entre la parte de las centenas h y una parte t menor que cien; y después, en la
definición de convertir2, descomponemos t en la parte de las decenas y en otra parte menor que
diez.
Ahora ya podemos pasar a la siguiente y última etapa en la que n puede tener 6 dígitos y por lo tanto
0 < n < 1000000. De forma similar al procedimiento anterior, descomponemos n en dos números m
y h, donde m es la parte de los millares y h es un número menor que mil. Tenemos entonces:
convertir6 :: Integer → String
convertir6 = combinar6 . digitos6
(*Completar las definiciones de digitos6 y combinar6:*) 22

La función convertir requerida es, precisamente, la función convertir6.

7
Programación funcional – Haskell
Notas al pie

8
1
False || x = x
True || x = True
2
x ≠ y = not (x == y)
3
bisiesto x = if (mod x 100 == 0) then (mod x 400 == 0) else (mod x 4 == 0)
4
ordena1::(Int,Int,Int)->(Int,Int,Int)
ordena1 (x, y, z) = if x > y then (y, x, z) else (x, y, z)
ordena2 :: (Int,Int,Int)->(Int,Int,Int)
ordena2 (x, y, z) = if y > z then (x, z, y) else (x, y, z)
ordena3(x,y,z)=ordena1(ordena2(ordena1(x,y,z)))
ordena3 (x, y, z) = ordena1 ( ordena2 ( ordena1 (x, y, z)))
triangulo2::(Int,Int,Int)->String
triangulo2 = triangulo ( ordena3 (x, y, z))
5
isLower :: Char->Bool
isLower c = (‘a’ <= c) && (c <= ‘z’)
6
aMayuscula :: Char -> Char
aMayuscula c = if isLower c then chr (base + ord c) else c
where base = ord ‘A’ – ord ‘a’
7
siguienteLetra :: Char->Char
siguienteLetra c = if c == ‘Z’ then ‘A’ else if c == ‘z’ then ‘a’ else chr (ord ( c ) + 1)
8
valorNum :: Char->Int
valorNum c = ord (c ) – ord (‘0’)
9
diaDespues :: Dia->Dia
diaDespues d = if d == Sab then Dom else toEnum (fromEnum ( d ) + 1)
10
data Direccion = Este | Oeste | Sur | Norte deriving (Eq,Ord,Enum,Show)
Invertir :: Direccion -> Direccion
invertir x = if mod (fromEnum (x)) 2 == 0 then toEnum(fromEnum (x) + 1) else toEnum(fromEnum (x) - 1)
11
last :: forall a.[a] -> a
12
head :: forall a.[a] -> a
head (x : xs) = x
13
tail :: forall a.[a] → [a]
tail (x : xs) = xs
14
nros :: Integer -> [Integer]
nros (n) = if n > 1 then nros (n – 1)++[n] else [n]
15
init [1, 2]
= {notación}
(1 : (2 : [ ] ))
= {definición de init}
(1 : init (2: [ ] ))
= {definición de init}
(1 : [ ] )
= {notación}
[1] devuelve la lista que queda después de eliminar el último elemento
16
reverse :: forall a.[a] → [a]
reverse [ ]=[ ]
reverse (x : xs) = reverse xs ++ [x]
17
take (n + 1) (x : xs) = x : take n xs drop 0 xs = xs drop (n + 1) [ ] = [ ]
18
even :: Integer → Bool
even x = if x mod 2 == 0 then True else False
19
sum :: (Num α) ⇒ [α] → α
sum [ ] =0
sum (x : xs) = x + sum xs
20
upto :: Integer → Integer → [Integer]
upto m n = if m > n then [ ] else m : upto (m + 1) n
21
α
x
22
digitos6 :: Integer → (Integer, Integer)
digitos6 n = (n div 1000, n mod 1000)
combinar6 :: (Integer, Integer) → String
combinar6 (0, h+1) = convertir3 (h+1)
combinar6 (m+1, 0) = convertir3 (m+1) ++ “⊔mil”
combinar6 (m+1, h+1) = convertir3 (m+1) ++ “⊔mil” ++ convertir3 (h+1)

Das könnte Ihnen auch gefallen