Tema 11: Aplicaciones de programación funcional
Índice
1. El juego de cifras y letras
1.1. Introducción
1.1.1. Presentación del juego
- Cifras y letras es un programa de la televisión que incluye un juego numérico.
- La esencia del juego es la siguiente: Dada una sucesión de números naturales y un número objetivo, intentar construir una expresión cuyo valor es el objetivo combinando los números de la sucesión usando suma, resta, multiplicación, división y paréntesis. Cada número de la sucesión puede usarse como máximo una vez. Además, todos los números, incluyendo los resultados intermedios tienen que ser enteros positivos (1,2,3,…).
- Ejemplos:
- Dada la sucesión 1, 3, 7, 10, 25, 50 y el objetivo 765, una solución es (1+50)*(25−10).
- Para el problema anterior, existen 780 soluciones.
- Con la sucesión anterior y el objetivo 831, no hay solución.
1.1.2. Formalización del problema: Operaciones
Las operaciones son sumar, restar, multiplicar o dividir.
data Op = Sum | Res | Mul | Div instance Show Op where show Sum = "+" show Res = "-" show Mul = "*" show Div = "/"
ops
es la lista de las operaciones.ops :: [Op] ops = [Sum,Res,Mul,Div]
1.1.3. Operaciones válidas
(valida o x y)
se verifica si la operacióno
aplicada a los números naturalesx
ey
da un número natural. Por ejemplo,valida Res 5 3 == True valida Res 3 5 == False valida Div 6 3 == True valida Div 6 4 == False
La definición es
valida :: Op -> Int -> Int -> Bool valida Sum _ _ = True valida Res x y = x > y valida Mul _ _ = True valida Div x y = y /= 0 && x `mod` y == 0
1.1.4. Aplicación de operaciones
(aplica o x y)
es el resultado de aplicar la operacióno
a los números naturalesx
ey
. Por ejemplo,aplica Sum 2 3 == 5 aplica Div 6 3 == 2
La definición es
aplica :: Op -> Int -> Int -> Int aplica Sum x y = x + y aplica Res x y = x - y aplica Mul x y = x * y aplica Div x y = x `div` y
La definición anterior se puede simplificar como sigue
aplica :: Op -> Int -> Int -> Int aplica Sum = (+) aplica Res = (-) aplica Mul = (*) aplica Div = div
1.1.5. Expresiones
Las expresiones son números enteros o aplicaciones de operaciones a dos expresiones.
data Expr = Num Int | Apl Op Expr Expr instance Show Expr where show (Num n) = show n show (Apl o i d) = parentesis i ++ show o ++ parentesis d where parentesis (Num n) = show n parentesis e = "(" ++ show e ++ ")"
Ejemplo: Expresión correspondiente a (1+50)*(25−10)
ejExpr :: Expr ejExpr = Apl Mul e1 e2 where e1 = Apl Sum (Num 1) (Num 50) e2 = Apl Res (Num 25) (Num 10)
1.1.6. Números de una expresión
(numeros e)
es la lista de los números que aparecen en la expresióne
. Por ejemplo,λ> numeros (Apl Mul (Apl Sum (Num 2) (Num 3)) (Num 7)) [2,3,7]
La definición es
numeros :: Expr -> [Int] numeros (Num n) = [n] numeros (Apl _ l r) = numeros l ++ numeros r
1.1.7. Valor de una expresión
(valor e)
es la lista formada por el valor de la expresióne
si todas las operaciones para calcular el valor dee
son números positivos y la lista vacía en caso contrario. Por ejemplo,valor (Apl Mul (Apl Sum (Num 2) (Num 3)) (Num 7)) == [35] valor (Apl Res (Apl Sum (Num 2) (Num 3)) (Num 7)) == [] valor (Apl Sum (Apl Res (Num 2) (Num 3)) (Num 7)) == []
La definición es
valor :: Expr -> [Int] valor (Num n) = [n | n > 0] valor (Apl o i d) = [aplica o x y | x <- valor i , y <- valor d , valida o x y]
1.1.8. Funciones combinatorias: Sublistas
(sublistas xs)
es la lista de las sublistas dexs
. Por ejemplo,λ> sublistas "bc" ["","c","b","bc"] λ> sublistas "abc" ["","c","b","bc","a","ac","ab","abc"]
La definición es
sublistas :: [a] -> [[a]] sublistas [] = [[]] sublistas (x:xs) = yss ++ map (x:) yss where yss = sublistas xs
1.1.9. Funciones combinatoria: Intercalado
(intercala x ys)
es la lista de las listas obtenidas intercalandox
entre los elementos deys
. Por ejemplo,intercala 'x' "bc" == ["xbc","bxc","bcx"] intercala 'x' "abc" == ["xabc","axbc","abxc","abcx"]
La definición es
intercala :: a -> [a] -> [[a]] intercala x [] = [[x]] intercala x (y:ys) = (x:y:ys) : map (y:) (intercala x ys)
1.1.10. Funciones combinatoria: Permutaciones
(permutaciones xs)
es la lista de las permutaciones dexs
. Por ejemplo,λ> permutaciones "bc" ["bc","cb"] λ> permutaciones "abc" ["abc","bac","bca","acb","cab","cba"]
La definición es
permutaciones :: [a] -> [[a]] permutaciones [] = [[]] permutaciones (x:xs) = concat (map (intercala x) (permutaciones xs))
- Nota: La función
permutaciones
es equivalente apermutations
definida enData.List
.
1.1.11. Funciones combinatoria: Elecciones
(elecciones xs)
es la lista formada por todas las sublistas dexs
en cualquier orden. Por ejemplo,λ> elecciones "abc" ["","c","b","bc","cb","a","ac","ca","ab","ba", "abc","bac","bca","acb","cab","cba"]
La definición es
elecciones :: [a] -> [[a]] elecciones xs = concat (map permutaciones (sublistas xs))
Nota: La definición anterior se puede simplificar como sigue
elecciones :: [a] -> [[a]] elecciones = concaMap permutaciones . sublistas
1.1.12. Reconocimiento de las soluciones
(solucion e ns n)
se verifica si la expresióne
es una solución para la sucesiónns
y objetivon
; es decir. si los números dee
es una posible elección dens
y el valor dee
esn
. Por ejemplo,solucion ejExpr [1,3,7,10,25,50] 765 == True
La definición es
solucion :: Expr -> [Int] -> Int -> Bool solucion e ns n = elem (numeros e) (elecciones ns) && valor e == [n]
1.2. Búsqueda de la solución por fuerza bruta
1.2.1. Divisiones de una lista
(divisiones xs)
es la lista de las divisiones dexs
en dos listas no vacías. Por ejemplo,λ> divisiones "bcd" [("b","cd"),("bc","d")] λ> divisiones "abcd" [("a","bcd"),("ab","cd"),("abc","d")]
La definición es
divisiones :: [a] -> [([a],[a])] divisiones [] = [] divisiones [_] = [] divisiones (x:xs) = ([x],xs) : [(x:is,ds) | (is,ds) <- divisiones xs]
1.2.2. Expresiones construibles
(expresiones ns)
es la lista de todas las expresiones construibles a partir de la lista de númerosns
. Por ejemplo,λ> expresiones [2,3,5] [2+(3+5),2-(3+5),2*(3+5),2/(3+5),2+(3-5),2-(3-5), 2*(3-5),2/(3-5),2+(3*5),2-(3*5),2*(3*5),2/(3*5), 2+(3/5),2-(3/5),2*(3/5),2/(3/5),(2+3)+5,(2+3)-5, ...
La definición es
expresiones :: [Int] -> [Expr] expresiones [] = [] expresiones [n] = [Num n] expresiones ns = [e | (is,ds) <- divisiones ns , i <- expresiones is , d <- expresiones ds , e <- combina i d]
1.2.3. Combinación de expresiones
(combina e1 e2)
es la lista de las expresiones obtenidas combinando las expresionese1
ye2
con una operación. Por ejemplo,λ> combina (Num 2) (Num 3) [2+3,2-3,2*3,2/3]
La definición es
combina :: Expr -> Expr -> [Expr] combina e1 e2 = [Apl o e1 e2 | o <- ops]
1.2.4. Búsqueda de las soluciones
(soluciones ns n)
es la lista de las soluciones para la sucesiónns
y objetivon
calculadas por fuerza bruta. Por ejemplo,λ> soluciones [1,3,7,10,25,50] 765 [3*((7*(50-10))-25), ((7*(50-10))-25)*3, ... λ> length (soluciones [1,3,7,10,25,50] 765) 780 λ> length (soluciones [1,3,7,10,25,50] 831) 0
La definición es
soluciones :: [Int] -> Int -> [Expr] soluciones ns n = [e | ns' <- elecciones ns , e <- expresiones ns' , valor e == [n]]
1.2.5. Estadísticas de la búsqueda por fuerza bruta
Estadísticas:
λ> :set +s λ> head (soluciones [1,3,7,10,25,50] 765) 3*((7*(50-10))-25) (8.47 secs, 400306836 bytes) λ> length (soluciones [1,3,7,10,25,50] 765) 780 (997.76 secs, 47074239120 bytes) λ> length (soluciones [1,3,7,10,25,50] 831) 0 (1019.13 secs, 47074535420 bytes) λ> :unset +s
1.3. Búsqueda combinando generación y evaluación
1.3.1. Resultados
Resultado
es el tipo de los pares formados por expresiones válidas y su valor.type Resultado = (Expr,Int)
(resultados ns)
es la lista de todos los resultados construibles a partir de la lista de númerosns
. Por ejemplo,λ> resultados [2,3,5] [(2+(3+5),10), (2*(3+5),16), (2+(3*5),17), (2*(3*5),30), ((2+3)+5,10), ((2+3)*5,25), ((2+3)/5,1), ((2*3)+5,11), ((2*3)-5,1), ((2*3)*5,30)]
La definición es
resultados :: [Int] -> [Resultado] resultados [] = [] resultados [n] = [(Num n,n) | n > 0] resultados ns = [res | (is,ds) <- divisiones ns , ix <- resultados is , dy <- resultados ds , res <- combina' ix dy]
1.3.2. Combinación de resultados
(combina' r1 r2)
es la lista de los resultados obtenidos combinando los resultadosr1
yr2
con una operación. Por ejemplo,λ> combina' (Num 2,2) (Num 3,3) [(2+3,5),(2*3,6)] λ> combina' (Num 3,3) (Num 2,2) [(3+2,5),(3-2,1),(3*2,6)] λ> combina' (Num 2,2) (Num 6,6) [(2+6,8),(2*6,12)] λ> combina' (Num 6,6) (Num 2,2) [(6+2,8),(6-2,4),(6*2,12),(6/2,3)]
La definición es
combina' :: Resultado -> Resultado -> [Resultado] combina' (i,x) (d,y) = [(Apl o i d, aplica o x y) | o <- ops , valida o x y]
1.3.3. Búsqueda combinando generación y evaluación
(soluciones' ns n)
es la lista de las soluciones para la sucesiónns
y objetivon
calculadas intercalando generación y evaluación. Por ejemplo,λ> head (soluciones' [1,3,7,10,25,50] 765) 3*((7*(50-10))-25) λ> length (soluciones' [1,3,7,10,25,50] 765) 780 λ> length (soluciones' [1,3,7,10,25,50] 831) 0
La definición es
soluciones' :: [Int] -> Int -> [Expr] soluciones' ns n = [e | ns' <- elecciones ns , (e,m) <- resultados ns' , m == n]
1.3.4. Estadísticas de la búsqueda combinada
Estadísticas:
λ> head (soluciones' [1,3,7,10,25,50] 765) 3*((7*(50-10))-25) (0.81 secs, 38804220 bytes) λ> length (soluciones' [1,3,7,10,25,50] 765) 780 (60.73 secs, 2932314020 bytes) λ> length (soluciones' [1,3,7,10,25,50] 831) 0 (61.68 secs, 2932303088 bytes)
1.4. Búsqueda mejorada mediante propiedades algebraicas
1.4.1. Aplicaciones válidas
(valida' o x y)
se verifica si la operacióno
aplicada a los números naturalesx
ey
da un número natural, teniendo en cuenta las siguientes reducciones algebraicasx + y = y + x x * y = y * x x * 1 = x 1 * y = y x / 1 = x
La definición es
valida' :: Op -> Int -> Int -> Bool valida' Sum x y = x <= y valida' Res x y = x > y valida' Mul x y = x /= 1 && y /= 1 && x <= y valida' Div x y = y /= 0 && y /= 1 && x `mod` y == 0
1.4.2. Resultados válidos construibles
(resultados' ns)
es la lista de todos los resultados válidos construibles a partir de la lista de númerosns
. Por ejemplo,λ> resultados' [5,3,2] [(5-(3-2),4),((5-3)+2,4),((5-3)*2,4),((5-3)/2,1)]
La definición es
resultados' :: [Int] -> [Resultado] resultados' [] = [] resultados' [n] = [(Num n,n) | n > 0] resultados' ns = [res | (is,ds) <- divisiones ns , ix <- resultados' is , dy <- resultados' ds , res <- combina'' ix dy]
1.4.3. Combinación de resultados válidos
(combina'' r1 r2)
es la lista de los resultados válidos obtenidos combinando los resultadosr1
yr2
con una operación. Por ejemplo,combina'' (Num 2,2) (Num 3,3) => [(2+3,5),(2*3,6)] combina'' (Num 3,3) (Num 2,2) => [(3-2,1)] combina'' (Num 2,2) (Num 6,6) => [(2+6,8),(2*6,12)] combina'' (Num 6,6) (Num 2,2) => [(6-2,4),(6/2,3)]
La definición es
combina'' :: Resultado -> Resultado -> [Resultado] combina'' (i,x) (d,y) = [(Apl o i d, aplica o x y) | o <- ops , valida' o x y]
Búsqueda mejorada mediante propiedades algebraicas
(soluciones'' ns n)
es la lista de las soluciones para la sucesiónns
y objetivon
calculadas intercalando generación y evaluación y usando las mejoras aritméticas. Por ejemplo,λ> head (soluciones'' [1,3,7,10,25,50] 765) 3*((7*(50-10))-25) λ> length (soluciones'' [1,3,7,10,25,50] 765) 49 λ> length (soluciones'' [1,3,7,10,25,50] 831) 0
La definición es
soluciones'' :: [Int] -> Int -> [Expr] soluciones'' ns n = [e | ns' <- elecciones ns , (e,m) <- resultados' ns' , m == n]
1.4.4. Estadísticas de la búsqueda mejorada
Estadísticas:
λ> head (soluciones'' [1,3,7,10,25,50] 765) 3*((7*(50-10))-25) (0.40 secs, 16435156 bytes) λ> length (soluciones'' [1,3,7,10,25,50] 765) 49 (10.30 secs, 460253716 bytes) λ> length (soluciones'' [1,3,7,10,25,50] 831) 0 (10.26 secs, 460253908 bytes)§
1.4.5. Comparación de las búsquedas
- Caso 1: Comparación de las búsquedad problema de dados [1,3,7,10,25,50] obtener 765.
Búsqueda de la primera solución:
+---------------------+ | segs. | bytes | +--------------+-------+-------------+ | soluciones | 8.47 | 400.306.836 | | soluciones' | 0.81 | 38.804.220 | | soluciones'' | 0.40 | 16.435.156 | +--------------+-------+-------------+
Búsqueda de todas las soluciones:
+--------+----------------+ | segs. | bytes | +--------------+--------+----------------+ | soluciones | 997.76 | 47.074.239.120 | | soluciones' | 60.73 | 2.932.314.020 | | soluciones'' | 10.30 | 460.253.716 | +--------------+--------+----------------+
Caso 2: Comprobación de que dados [1,3,7,10,25,50] no puede obtenerse 831
+---------+----------------+ | segs. | bytes | +--------------+---------+----------------+ | soluciones | 1019.13 | 47.074.535.420 | | soluciones' | 61.68 | 2.932.303.088 | | soluciones'' | 10.26 | 460.253.908 | +--------------+---------+----------------+
2. El problema de las reinas
- Enunciado: Colocar N reinas en un tablero rectangular de dimensiones N por N de forma que no se encuentren más de una en la misma línea: horizontal, vertical o diagonal.
El tablero se representa por una lista de números que indican las filas donde se han colocado las reinas. Por ejemplo,
[3,5]
indica que se han colocado las reinas(1,3)
y(2,5)
.type Tablero = [Int]
reinas n
es la lista de soluciones del problema de las N reinas. Por ejemplo,reinas 4 == [[3,1,4,2],[2,4,1,3]]
La primera solución
[3,1,4,2]
se interpreta como|---|---|---|---| | | R | | | |---|---|---|---| | | | | R | |---|---|---|---| | R | | | | |---|---|---|---| | | | R | | |---|---|---|---|
La definición es
reinas :: Int -> [Tablero] reinas n = aux n where aux 0 = [[]] aux m = [r:rs | rs <- aux (m-1), r <- ([1..n] \\ rs), noAtaca r rs 1]
noAtaca r rs d
se verifica si la reinar
no ataca a ninguna de las de la listars
donde la primera de la lista está a una distancia horizontald
.noAtaca :: Int -> Tablero -> Int -> Bool noAtaca _ [] _ = True noAtaca r (a:rs) distH = abs(r-a) /= distH && noAtaca r rs (distH+1)
3. Números de Hamming
- Enunciado: Los números de Hamming forman una sucesión estrictamente creciente de números que cumplen las siguientes condiciones:
- El número 1 está en la sucesión.
- Si x está en la sucesión, entonces 2x, 3x y 5x también están.
- Ningún otro número está en la sucesión.
hamming
es la sucesión de Hamming. Por ejemplo,take 12 hamming == [1,2,3,4,5,6,8,9,10,12,15,16]
La definición es
hamming :: [Int] hamming = 1 : mezcla3 [2*i | i <- hamming] [3*i | i <- hamming] [5*i | i <- hamming]
mezcla3 xs ys zs
es la lista obtenida mezclando las listas ordenadasxs
,ys
yzs
y eliminando los elementos duplicados. Por ejemplo,λ> mezcla3 [2,4,6,8,10] [3,6,9,12] [5,10] [2,3,4,5,6,8,9,10,12]
La definición es
mezcla3 :: [Int] -> [Int] -> [Int] -> [Int] mezcla3 xs ys zs = mezcla2 xs (mezcla2 ys zs)
mezcla2 xs ys
es la lista obtenida mezclando las listas ordenadasxs
eys
y eliminando los elementos duplicados. Por ejemplo,mezcla2 [2,4,6,8,10,12] [3,6,9,12] == [2,3,4,6,8,9,10,12]
La definición es
mezcla2 :: [Int] -> [Int] -> [Int] mezcla2 p@(x:xs) q@(y:ys) | x < y = x:mezcla2 xs q | x > y = y:mezcla2 p ys | otherwise = x:mezcla2 xs ys mezcla2 [] ys = ys mezcla2 xs [] = xs
4. Material complementario
El código del tema se encuentra en este enlace.
Este tema también se encuentra en los siguientes formatos:
5. Bibliografía
- G. Hutton. Programming in Haskell. Cambridge University Press, 2007.
- Cap. 11: The countdown problem.
- B.C. Ruiz, F. Gutiérrez, P. Guerrero y J.E. Gallardo. Razonando con
Haskell. Thompson, 2004.
- Cap. 13: Puzzles y solitarios.