1. Tres enteros positivos x, y, z constituyen una terna pitagórica si x2+y2=z2, es decir, si son los lados
de un triángulo rectángulo.
a) Define la función
esTerna :: Integer -> Integer -> Integer -> Bool
que compruebe si tres valores forman una terna pitagórica. Por ejemplo:
Main> esTerna 3 4 5 Main> esTerna 3 4 6
True False
b)
Es fácil demostrar que para cualesquiera x e y enteros positivos con
x>y, la terna (x2-y2, 2xy, x2+y2) es pitagórica. Usando esto,
escribe una función terna que tome dos parámetros y devuelva una
terna pitagórica. Por ejemplo:
Main> terna 3 1
(8,6,10)
Main> esTerna 8 6 10
True
c) Lee y entiende la siguiente propiedad, para comprobar que todas las ternas generadas por la
función terna son pitagóricas:
p_ternas x y = x>0 && y>0 && x>y ==> esTerna l1 l2 h where (l1,l2,h) = terna x y
d) Comprueba esta propiedad usando QuickCheck (recuerda importar Test.QuickCheck al principio
de tu programa y copiar la propiedad en tu fichero). Verás que la respuesta es parecida a:
Main> quickCheck p_ternas
*** Gave up!Passed only 62 tests
lo que indica que, aunque sólo se generaron 62 casos de pruebas con las condiciones precisas,
todos estos los casos pasaron la prueba.
esTerna :: Integer -> Integer -> Integer -> Bool
esTerna x y z = x^2+y^2==z^2
terna :: Integer -> Integer -> (Integer,Integer,Integer)
terna x y = if x>y then ( x^2 - y^2 , 2*x*y , x^2 + y^2) else error "no hay terna"
--Otra forma
terna x y =( x^2-y^2, 2*x*y, x^2+y^2) --el uso del if then else equivale al uso de guardas
|x>y
|otherwise error "no hay terna"
p_ternas x y = x>0 && y>0 && x>y ==> esTerna l1 l2 h
where
(l1,l2,h) = terna x y
-- *Main> quickCheck p_ternas
-- *** Gave up! Passed only 77 tests.
2. Define una función polimórfica intercambia :: (a,b) -> (b,a) que intercambie de posición los datos de la tupla:
Main> intercambia (1,True) Main> intercambia ('X','Y')
(True,1) ('Y','X')
intercambia :: (a,b)->(b,a)
intercambia (x,y) = (y,x)
3. Este ejercicio versa sobre ordenación de tuplas.
a) Define una función sobrecargada para tipos con orden ordena2 :: Ord a => (a,a) -> (a,a)
que tome una tupla con dos valores del mismo tipo y la devuelva ordenada de menor a mayor:
Main> ordena2 (10,3) Main> ordena2 ('a','z')(3,10) ('a','z')
Copia en tu fichero las siguientes propiedades relativas a la función ordena2:
p1_ordena2 x y = enOrden (ordena2 (x,y))
where enOrden (x,y) = x<=y
p2_ordena2 x y = mismosElementos (x,y) (ordena2 (x,y))
where
mismosElementos (x,y) (a,b) = (x==a && y==b) || (x==b && y==a)
(o, alternativamente, (x,y)==(a,b)||(x,y)==(b,a))
Entiende lo que cada una significa, y compruébalas usando QuickCheck.
b) Define una función sobrecargada para tipos con orden
ordena3 :: Ord a => (a,a,a) -> (a,a,a)
que tome una tupla con tres valores del mismo tipo y la devuelva ordenada, con los elementos de
menor a mayor:
Main> ordena3 (10,3,7) (3,7,10)
c) Escribe propiedades análogas a las del apartado anterior pero para esta función, y compruébalas
usando QuickCheck.
ordena2 :: Ord a => (a,a) -> (a,a)
ordena2 (x,y)
| x<=y = (x,y)
| otherwise = (y,x)
p1_ordena2 x y = enOrden (ordena2 (x,y))
where enOrden (x,y) = x<=y
p2_ordena2 x y = mismosElementos (x,y) (ordena2 (x,y))
where
mismosElementos (x,y) (a,b) = (x==a && y==b) || (x==b && y==a)
--otra forma
mismosElementos (x,y) (a,b) = (x,y)==(a,b) || (x,y)==(b,a)
ordena3 :: Ord a => (a,a,a) -> (a,a,a)
ordena3 (x,y,z) |x>y = ordena3(y,x,z)
|y>z = ordena3(x,z,y)
|x>z = ordena3(z,x,y)
|otherwise = (x,y,z)
p1_ordena3 x y z = enOrden(ordena3(x,y,z))
where enOrden(x,y,z) = x<=y && y<=z
4. Aunque ya existe una función predefinida (max :: Ord a => a -> a -> a) para calcular el
máximo de dos valores, el objetivo de este ejercicio es que definas tu propia versión de dicha
función.
a) Como no está permitido redefinir una función predefinida, define una nueva y llámala
max2 ::Ord a => a -> a -> a
de forma que satisfaga:
Main> 10 `max2` 7 Main> max2 'a' 'z' 10 'z'
b) Define las siguientes propiedades que debería verificar tu función max2 y compruébalas con
QuickCheck (recuerda importar Test.QuickCheck al principio de tu programa):
i. p1_max2: el máximo de dos números x e y coincide o bien con x o bien con y.
ii. p2_max2: el máximo de x e y es mayor o igual que x , así como mayor o igual que y.
iii. p3_max2: si x es mayor o igual que y, entonces el máximo de x e y es x.
iv. p4_max2: si y es mayor o igual que x, entonces el máximo de x e y es y.
max2:: Ord a => a -> a -> a
max2 x y
| x>y = x
| otherwise = y
p1_max2 x y = True ==> max2 x y == x || max2 x y == y
p2_max2 x y = True ==> max2 x y >= x || max2 x y >= y
p3_max2 x y = x>=y ==> max2 x y == x
p4_max2 x y = y>=x ==> max2 x y == y
5. Define una función sobrecargada para tipos con orden
entre :: Ord a => a -> (a,a) -> a
que tome un valor x además de una tupla con dos valores (max,min) y compruebe si x pertenece al
intervalo determinado por min y max, es decir, si x ∈ [min,max], devolviendo True o False según
corresponda. Por ejemplo:
Main> 5 `entre` (1,10) Main> entre 'z' ('a','d')
True False
entre :: Ord a => a -> (a, a) -> Bool
entre x (p,q) | x>p && x<q = True
| otherwise = False
--Otra forma
entre x(p,q)=x>=y && x<=q
6. Define una función sobrecargada para tipos con igualdad
iguales3 :: Eq a => (a,a,a) -> Bool
que tome una tupla con tres valores del mismo tipo y devuelva True si todos son iguales. Por
ejemplo:
Main> iguales3 ('z','a','z')
False
Main> iguales3 (5+1,6,2*3)
True
iguales3 :: Eq a => (a,a,a)-> Bool
iguales3 (x,y,z)
| x==y && y==z = True
| otherwise = False
--Otra forma
ifguales3 (x,y,z) = x==y && y==z
7. Recuerda que el cociente y el resto de la división de enteros se corresponde con las funciones
predefinidas div y mod.
a) Define una función descomponer que, dada una cantidad positiva de segundos, devuelva la
descomposición en horas, minutos y segundos en forma de tupla, de modo que los minutos y
segundos de la tupla estén en el rango 0 a 59. Por ejemplo:
descomponer 5000 (1,23,20) descomponer 100 (0,1,40)
Para ello, completa la siguiente definición:
type TotalSegundos = Integer
type Horas = Integer
type Minutos = Integer
type Segundos = Integer
descomponer :: TotalSegundos -> (Horas,Minutos,Segundos)
descomponer x = (horas, minutos, segundos)
where
horas = ...
...
b) Comprueba la corrección de tu función verificando con QuickCheck que cumple la siguiente
propiedad:
p_descomponer x = x>=0 ==> h*3600 + m*60 + s == x
&& entre m (0,59)
&& entre s (0,59)
where (h,m,s) = descomponer x
-- Para este ejercicio nos interesa utilizar la función predefinida en Prelude:
-- divMod :: a -> a -> (a, a)
-- que calcula simultáneamente el cociente y el resto:
--
-- *Main> divMod 30 7
-- (4,2)
type TotalSegundos = Integer
type Horas = Integer
type Minutos = Integer
type Segundos = Integer
descomponer :: TotalSegundos -> (Horas,Minutos,Segundos)
descomponer x = (horas, minutos, segundos)
where
(horas,resto) = divMod x 3600
(minutos,segundos) = divMod resto 60
p_descomponer x = x>=0 ==> h*3600 + m*60 + s == x
&& m `entre` (0,59)
&& s `entre` (0,59)
where (h,m,s) = descomponer x
-- *Main> quickCheck p_descomponer
-- +++ OK, passed 100 tests.
8. Sea la siguiente definición que representa que un euro son 166.386 pesetas:
unEuro :: Double
unEuro = 166.386
a) Define una función pesetasAEuros que convierta una cantidad (de tipo Double) de pesetas en los
correspondientes euros. Por ejemplo:
pesetasAEuros 1663.86 10.0
b) Define la función eurosAPesetas que convierta euros en pesetas. Por ejemplo:
eurosAPesetas 10 1663.86
c) Sea la siguiente propiedad, que establece que si pasamos una cantidad de pesetas a euros y los
euros los volvemos a pasar a pesetas, obtenemos las pesetas originales (es decir, que las funciones
son inversas):
p_inversas x = eurosAPesetas (pesetasAEuros x) == x
Compruébala con QuickCheck para ver que no se verifica. ¿por qué falla? (pista: estamos trabajando
con números flotantes).
unEuro :: Double
unEuro = 166.386
pesetasAEuros :: Double -> Double
pesetasAEuros x = x/unEuro
eurosAPesetas :: Double -> Double
eurosAPesetas x = x*unEuro
p_inversas :: Double -> Bool
p_inversas x = eurosAPesetas (pesetasAEuros x) ~= x
--
p_inversas x no se cumple debido a que las pruebas las ejecuta con
numeros en punto flotante y pueden variar unas milesimas y no encajar en
la igualdad
9. Sea el siguiente operador que comprueba si dos valores de tipo Double son aproximadamente
iguales:
infix 4 ~=(~=) :: Double -> Double -> Bool
x ~= y = abs (x-y) < epsilon
where epsilon = 1/1000
Por ejemplo: (1/3) ~= 0.33 False (1/3) ~= 0.333 True
Copia esta definición de operador en tu fichero de programa, y cambia la propiedad p_inversas
del ejercicio anterior para que verifique que si pasamos una cantidad de pesetas a euros y los euros
los volvemos a pasar a pesetas, obtenemos las pesetas originales aproximadamente. Comprueba
con QuickCheck que esta propiedad sí se verifica.
infix 4 ~=
(~=) :: Double -> Double -> Bool
x ~= y = abs (x-y) < epsilon
where epsilon = 1/1000
10. Consideremos la ecuación de segundo grado ax2 + bx + c = 0.
a) Define una función raíces que tome tres parámetros (correspondientes a los coeficientes a, b y c
de la ecuación) y devuelva una tupla con las dos soluciones reales de la ecuación (para calcular la
raíz cuadrada, usa la función predefinida sqrt). Recuerda que el discriminante se define como b24ac
y que la ecuación tiene raíces reales si el discriminante no es negativo. Por ejemplo:
raíces 1 (-2) 1.0 (1.0,1.0)
raíces 1.0 2 4 Exception: Raíces no reales
b) Sea la siguiente propiedad que comprueba que las valores devueltos por la función raíces son
efectivamente raíces de la ecuación:
p1_raíces a b c = esRaíz r1 && esRaíz r2
where
(r1,r2) = raíces a b c
esRaíz r = a*r^2 + b*r + c ~= 0
Comprueba esta propiedad con QuickCheck y verifica que falla. Piensa por qué falla, y añade
condiciones a la propiedad para que no falle, es decir, completa las interrogaciones:
p2_raíces a b c = ??????? && ?????? ==> esRaíz r1 && esRaíz r2
where
(r1,r2) = raíces a b c
esRaíz r = a*r^2 + b*r + c ~= 0
de forma que se verifique el siguiente diálogo:
Main> quickCheck p2_raíces
+++ OK, passed 100 tests
raices :: Double-> Double -> Double ->(Double,Double)
raices a b c
| b^2-4*a*c>0 = ((-b+sqrt (b^2- 4*a*c))/2*a ,(-b-sqrt (b^2- 4*a*c))/2*a)
| otherwise = error"Raices no reales"
p1_raices :: Double-> Double -> Double ->Bool
p1_raices a b c = esRaíz r1 && esRaíz r2
where
(r1,r2) = raices a b c
esRaíz r = a*r^2 + b*r + c ~= 0
--en cuanto una raiz sea negativa esto no funciona, ver
p2_raices :: Double-> Double -> Double ->Property
p2_raices a b c = (a,b,c)/=(0,0,0) && (b*b-4*a*c) >= 0 ==> esRaiz r1 && esRaiz r2
where
(r1,r2) = raices a b c
esRaiz r = a*r^2 + b*r + c ~= 0
11. Define una función esMúltiplo sobrecargada para tipos integrales que tome dos valores x e y, y
devuelva True si x es múltiplo de y. Por ejemplo:
esMúltiplo 9 3 True
esMúltiplo 7 3 False
esMultiplo :: (Integral a) => a->a->Bool
esMúltiplo x y = if mod x y ==0 then True else False
12. Define el operador de implicación lógica (==>>) :: Bool -> Bool -> Bool de forma que
sea asociativo a la izquierda, con precedencia menor que los operadores conjunción y disyunción:
Main> 3 < 1 ==>> 4 > 2
True
Main> 3 < 1 || 3 > 1 ==>> 4 > 2 && 4 < 2
False
Ayuda: puedes escribir ecuaciones directamente para la definición del operador, o bien patrones,
completando definiciones tales como:
False ==>> y = y
??? ==>> ??? = ???
infixl 1 ==>>
(==>>) :: Bool->Bool->Bool
False ==>> y=True
True ==>> y=y --Devuelve lo que valga y
13. Los años bisiestos son los años múltiplos de 4. Una excepción
a esta regla son los años múltiplos de 100, que sólo se consideran
bisiestos si además son múltiplos de 400. Define una función
esBisiesto que tome como parámetro un año y devuelva True si es bisiesto. Por ejemplo:
esBisiesto 1984 True
esBisiesto 1985 False
esBisiesto 1800 False
esBisiesto 2000 True
Ayuda: utiliza el operador de implicación lógica y la siguiente frase: “n es bisiesto si satisface las dos
condiciones siguientes: (a) es múltiplo de 4, y (b) si n es múltiplo de 100 entonces n es múltiplo de
400”.
esBisiesto :: Integral a => a-> Bool
esBisiesto x = esMultiploB x
where
esMultiploB x = esMúltiplo x 4 && esMúltiplo x 100 ==>> esMúltiplo x 400
--Otra forma
esBisiesto :: Integer->Bool
esBisiesto x = esMultiplo x 4 && (esMultiplo x 100 ==>> esMultiplo x 400)
14. Aunque ya existe en Haskell el operador predefinido (^) para calcular potencias, el objetivo de este
problema es que definas tus propias versiones recursiva de este operador.
a) A partir de la propiedad
define una función recursiva potencia' que tome un entero b y un exponente natural n y devuelva bn. Por ejemplo:
potencia 2 3 8
b) A partir de la siguiente propiedad:
define una función recursiva potencia' que tome un entero b y un exponente natural n y devuelva bn. Por ejemplo:
potencia' 2 3 8 potencia' 2 4 16
c) Comprueba con QuickCheck la corrección de ambas funciones mediante la siguiente propiedad:
p_pot b n = n>=0 ==> potencia b n == sol && potencia' b n == sol
where sol = b^n
d) Teniendo en cuenta que elevar al cuadrado equivale a realizar un producto, determina el número
de productos que realizan ambas funciones para elevar cierta base a un exponente n.
Ayuda: para analizar la eficiencia de potencia' considera exponentes que sean potencia de 2.
potencia :: Integer-> Integer -> Integer
otencia x y
| y== 0 = 1
| otherwise = potencia x (y-1) * x
potencia' :: Integer-> Integer -> Integer
potencia' x y
| mod y 2 == 0 = (potencia x (div y 2))^2
| otherwise = ((potencia x (div (y-1) 2))^2) * x
p_pot :: Integer->Integer->Property
p_pot b n = n>=0 ==> potencia b n == sol && potencia' b n == sol
where sol = b^n
15. Dado un conjunto finito con todos sus elementos diferentes, llamamos permutación a cada una de
las posibles ordenaciones de los elementos de dicho conjunto. Por ejemplo, para el conjunto
{1,2,3}, existen un total de 6 permutaciones de sus elementos: {1,2,3}, {1,3,2}, {2,1,3}, {2,3,1}, {3,1,2}
y {3,2,1}. El número de permutaciones posibles para un conjunto con n elementos viene dada por el
factorial de n (se suele escribir n!), que se define como el producto de todos los números naturales
menores o iguales a n. Escribe una función factorial que tome como parámetro un número
natural y devuelva su factorial. Dado que el factorial crece muy rápido, usa el tipo Integer, es
decir, factorial :: Integer -> Integer. Por ejemplo:
factorial 3 6 factorial 20 2432902008176640000
factorial :: Integer -> Integer
factorial x
| x==0 = 1
| otherwise = factorial (x-1) * x
16. Este ejercicio estudia la división entera (exacta) de números enteros.
a) Define una función divideA que compruebe si su primer argumento divide exactamente al
segundo. Por ejemplo:
2 `divideA` 10 True 4 `divideA` 10 False
b) Lee, entiende y comprueba con QuickCheck la siguiente propiedad referente a la función divideA:
p1_divideA x y = y/=0 && y `divideA` x ==> div x y * y == x
c) Escribe una propiedad p2_divideA para comprobar usando QuickCheck que si un número divide a
otros dos, también divide a la suma de ambos.
divideA :: Integer->Integer -> Bool
divideA x y = mod y x ==0
p1_divideA :: Integer->Integer -> Property
p1_divideA x y = y/=0 && y `divideA` x ==> div x y * y == x
p2_divideA :: Integer->Integer->Integer -> Property
p2_divideA a b c = a/=0 && b/=0 && c/=0 && divideA a b && divideA b c ==> divideA a (b+c)
17.
La mediana de un conjunto de valores es aquel valor tal que el 50% de
los valores del conjunto son menores o iguales a él, y los restantes
mayores o iguales. Queremos definir una función para
calcular la mediana de los valores de una tupla de cinco elementos
mediana :: Ord a => (a,a,a,a,a) -> a
de forma que se tenga:
mediana (3,20,1,10,50) 10
Observa que se satisface 1 , 3 ≤ 10 ≤ 20,50. Teniendo en cuenta este detalle, define la función a
través de ecuaciones con guardas, completando el siguiente esquema:
mediana :: Ord a => (a, a, a, a, a) -> a
mediana (x,y,z,t,u)
| x > z = mediana (z,y,x,t,u)
| y > z = mediana (x,z,y,t,u)
| z > u = mediana (x,y,u,t,z)
| z >t = mediana (x,y,u,z,t)
| otherwise = z
miércoles, 4 de septiembre de 2013
Tema 2 - Examples/Ejercicios
2. Define una función
máximoYresto :: Ord a => [a] -> (a,[a])
que “devuelva” en forma de par el máximo de la lista y los restantes elementos. Considera dos casos:
a.- el orden en que aparecen los restantes puede ser arbitrario:
máximoYresto [1,2,30,4,5,6,7] (30,[1,2,7,4,5,6])
b.- los restantes deben aparecer en el orden original
máximoYresto’ [1,2,30,4,5,6,7] (30,[1,2,4,5,6,7])
maximoYresto :: Ord a => [a] -> (a,[a])
maximoYresto [] = (null,[])
maximoYresto (x:xs) = if(x>=head xs) then maximoYresto (x:tail xs)
else maximoYresto xs
8. Un número natural p es primo si tiene exactamente dos divisores positivos distintos: 1 y p; por
tanto, 1 no es un número primo.
a) Define una función esPrimo para comprobar si un número es primo. Por ejemplo:
esPrimo 7 True esPrimo 10 False
b) Usando una lista por comprensión, define una función primosHasta que devuelva una lista
con todos los números primos menores o iguales a un valor dado. Por ejemplo:
primosHasta 50 [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]
c) Da otra definición (con nombre primosHasta') que use la función predefinida filter en vez
de la lista por comprensión.
d) Comprueba que las dos funciones que has definido se comportan del mismo modo
comprobando con QuickCheck la siguiente propiedad:
p1_primos x = primosHasta x == primosHasta' x
Nota: existen métodos más eficientes para calcular listas de primos( Criba de Eratóstenes.)
divisoresDe :: Integer -> [Integer]
divisoresDe n | n > 0 = [d | d <- [1..n], n `mod` d == 0]
| otherwise = error "no existen"
esPrimo :: Integer -> Bool
esPrimo x = length (divisoresDe x) ==2
-- B) Define una lista por compresión
primosHasta :: Integer -> [Integer]
primosHasta m = [x | x<-[1..m],esPrimo (x)]
-- C) Misma función usando filter
primosHasta':: Integer -> [Integer]
primosHasta' m = filter esPrimo [1..m]
-- D) quickCheck
p1_primos x = primosHasta x == primosHasta' x
14. La función predefinida
takeWhile :: (a -> Bool) -> [a] -> [a]
devuelve el prefijo más largo con los elementos de una lista (2º argumento) que cumplen una
condición (1er argumento). Por ejemplo:
takeWhile even [2,4,6,8,11,13,16,20] [2,4,6,8]
ya que el 11 es el primer elemento que no es par. Otro ejemplo de uso es:
takeWhile (<5) [2,4,6,1] [2,4]
ya que 6 es el primer elemento de la lista mayor o igual a 5. Para los mismos argumentos, la función
dropWhile suprime el prefijo que takeWhile devuelve. Por ejemplo:
dropWhile even [2,4,6,8,11,13,16,20] [11,13,16,20]
dropWhile (<5) [2,4,6,1] [6,1]
a) Usando estas funciones, define una función inserta que tome un elemento x y una lista xs que ya
está ordenada ascendentemente (asume que esta precondición se cumple), y que devuelva la lista
ordenada que se obtiene al insertar x en su posición adecuada dentro de xs. Por ejemplo:
inserta 5 [1,2,4,7,8,11 ] [1,2,4,5,7,8,11]
inserta 2 [1,2,4,7,8,11] [1,2,2,4,7,8,11]
inserta 0 [1,2,4,7,8,11] [0,1,2,4,7,8,11]
inserta 20 [1,2,4,7,8,11] [1,2,4,7,8,11,20]
b) Sin usar ninguna función auxiliar, define directamente y en forma recursiva la función inserta.
c) Lee, entiende y comprueba con QuickCheck la siguiente propiedad referente a la función inserta:
p1_inserta x xs = desconocida xs ==> desconocida (inserta x xs)
d) Podemos utilizar la función inserta que hemos definido para ordenar ascendentemente una lista
desordenada. Por ejemplo, si quisiéramos ordenar la lista [9,3,7], podríamos hacerlo evaluando
la expresión:
9 `inserta` (3 `inserta` (7 `inserta` []))
Razona por qué funciona este algoritmo para ordenar una lista.
e) Usando la función foldr y la función inserta, define una función ordena que tome una lista de
valores y la devuelva ordenada. Por ejemplo:
ordena [9,3,7] [3,7,9] ordena "abracadabra" "aaaaabbcdrr"
f) Escribe y comprueba con QuickCheck la siguiente propiedad: para cualquier lista xs, ordena xs es
una lista ordenada.
g) Demuestra la propiedad anterior por inducción sobre listas.
desconocida :: (Ord a) => [a] -> Bool
desconocida xs = and [ x<=y | (x,y) <- zip xs (tail xs) ]
-- A)
inserta :: Ord a => a -> [a] -> [a]
inserta x xs = (takeWhile (<x) (xs)) ++ [x] ++ (dropWhile (<=x) (xs))
-- B) inserta directamente y versión recursiva
inserta' :: Ord a => a -> [a] -> [a]
inserta' x [ ] = [x]
inserta' x (y:ys) = [y] ++ inserta x (ys)
-- C)
p_inserta x xs = length xs <= 10 && desconocida xs ==> desconocida (inserta x xs)
p_inserta' x xs = length xs <= 10 && desconocida xs ==> desconocida (inserta' x xs)
pinserta = quickCheck (p_inserta :: Int -> [Int] -> Property)
pinserta' = quickCheck (p_inserta' :: Int -> [Int] -> Property)
-- D) ¿por qué está ordenada la lista 9 `inserta` (3 `inserta` (7 `inserta` []))
-- E) algoritmo de ordenación a través de foldr e inserta
ordena :: Ord a => [a] -> [a]
ordena xs = foldr inserta [] xs
16. Aunque la función predefinida lcm (least common multiple) ya lo hace, el objetivo de este ejercicio es escribir una función mcm para calcular el mínimo común múltiplo de dos naturales.
a) Define una función múltiplosDe, que tome como parámetro un número mayor que cero y
devuelva una lista infinita con sus múltiplos positivos. Por ejemplo:
múltiplosDe 3 [3,6,9,12,15,...
Ayuda: usa la función predefinida iterate, descrita en un ejercicio anterior.
b) Define la función sobrecargada para tipos con orden
primeroComún :: Ord a => [a] -> [a] -> a
que tome dos listas ordenadas ascendentemente y devuelva el menor elemento común a ambas.
Por ejemplo:
primeroComún [1,2,5,7,9] [3,3,4,5,7] 5
c) El mínimo común múltiplo de dos naturales x e y es el menor natural positivo múltiplo de
ambos. Utilizando las funciones definidas en los apartados previos, escribe una función mcm
que calcule el mínimo común múltiplo de dos naturales usando esta definición.
d) Comprueba usando QuickCheck tu definición mediante esta propiedad:
p_mcm x y = x>=0 && y>=0 ==> mcm x y == lcm x y
-- A)
múltiplosDe :: Integer -> [Integer]
múltiplosDe x = map (*x) [1,2..]
-- B)
primeroComún :: Ord a => [a] -> [a] -> a
primeroComún (x:xs) (y:ys)
| x > y = primeroComún (x:xs) ys
| x< y = primeroComún (xs) (y:ys)
|otherwise = x
-- C)
mcm de dos naturales como menor mútiplo común positivo
mcm x y = primeroComún (múltiplosDe x) (múltiplosDe y)
-- D)
p_mcm x y = x>=0 && y>=0 ==> mcm x y == lcm x y
máximoYresto :: Ord a => [a] -> (a,[a])
que “devuelva” en forma de par el máximo de la lista y los restantes elementos. Considera dos casos:
a.- el orden en que aparecen los restantes puede ser arbitrario:
máximoYresto [1,2,30,4,5,6,7] (30,[1,2,7,4,5,6])
b.- los restantes deben aparecer en el orden original
máximoYresto’ [1,2,30,4,5,6,7] (30,[1,2,4,5,6,7])
maximoYresto :: Ord a => [a] -> (a,[a])
maximoYresto [] = (null,[])
maximoYresto (x:xs) = if(x>=head xs) then maximoYresto (x:tail xs)
else maximoYresto xs
8. Un número natural p es primo si tiene exactamente dos divisores positivos distintos: 1 y p; por
tanto, 1 no es un número primo.
a) Define una función esPrimo para comprobar si un número es primo. Por ejemplo:
esPrimo 7 True esPrimo 10 False
b) Usando una lista por comprensión, define una función primosHasta que devuelva una lista
con todos los números primos menores o iguales a un valor dado. Por ejemplo:
primosHasta 50 [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]
c) Da otra definición (con nombre primosHasta') que use la función predefinida filter en vez
de la lista por comprensión.
d) Comprueba que las dos funciones que has definido se comportan del mismo modo
comprobando con QuickCheck la siguiente propiedad:
p1_primos x = primosHasta x == primosHasta' x
Nota: existen métodos más eficientes para calcular listas de primos( Criba de Eratóstenes.)
divisoresDe :: Integer -> [Integer]
divisoresDe n | n > 0 = [d | d <- [1..n], n `mod` d == 0]
| otherwise = error "no existen"
esPrimo :: Integer -> Bool
esPrimo x = length (divisoresDe x) ==2
-- B) Define una lista por compresión
primosHasta :: Integer -> [Integer]
primosHasta m = [x | x<-[1..m],esPrimo (x)]
-- C) Misma función usando filter
primosHasta':: Integer -> [Integer]
primosHasta' m = filter esPrimo [1..m]
-- D) quickCheck
p1_primos x = primosHasta x == primosHasta' x
14. La función predefinida
takeWhile :: (a -> Bool) -> [a] -> [a]
devuelve el prefijo más largo con los elementos de una lista (2º argumento) que cumplen una
condición (1er argumento). Por ejemplo:
takeWhile even [2,4,6,8,11,13,16,20] [2,4,6,8]
ya que el 11 es el primer elemento que no es par. Otro ejemplo de uso es:
takeWhile (<5) [2,4,6,1] [2,4]
ya que 6 es el primer elemento de la lista mayor o igual a 5. Para los mismos argumentos, la función
dropWhile suprime el prefijo que takeWhile devuelve. Por ejemplo:
dropWhile even [2,4,6,8,11,13,16,20] [11,13,16,20]
dropWhile (<5) [2,4,6,1] [6,1]
a) Usando estas funciones, define una función inserta que tome un elemento x y una lista xs que ya
está ordenada ascendentemente (asume que esta precondición se cumple), y que devuelva la lista
ordenada que se obtiene al insertar x en su posición adecuada dentro de xs. Por ejemplo:
inserta 5 [1,2,4,7,8,11 ] [1,2,4,5,7,8,11]
inserta 2 [1,2,4,7,8,11] [1,2,2,4,7,8,11]
inserta 0 [1,2,4,7,8,11] [0,1,2,4,7,8,11]
inserta 20 [1,2,4,7,8,11] [1,2,4,7,8,11,20]
b) Sin usar ninguna función auxiliar, define directamente y en forma recursiva la función inserta.
c) Lee, entiende y comprueba con QuickCheck la siguiente propiedad referente a la función inserta:
p1_inserta x xs = desconocida xs ==> desconocida (inserta x xs)
d) Podemos utilizar la función inserta que hemos definido para ordenar ascendentemente una lista
desordenada. Por ejemplo, si quisiéramos ordenar la lista [9,3,7], podríamos hacerlo evaluando
la expresión:
9 `inserta` (3 `inserta` (7 `inserta` []))
Razona por qué funciona este algoritmo para ordenar una lista.
e) Usando la función foldr y la función inserta, define una función ordena que tome una lista de
valores y la devuelva ordenada. Por ejemplo:
ordena [9,3,7] [3,7,9] ordena "abracadabra" "aaaaabbcdrr"
f) Escribe y comprueba con QuickCheck la siguiente propiedad: para cualquier lista xs, ordena xs es
una lista ordenada.
g) Demuestra la propiedad anterior por inducción sobre listas.
desconocida :: (Ord a) => [a] -> Bool
desconocida xs = and [ x<=y | (x,y) <- zip xs (tail xs) ]
-- A)
inserta :: Ord a => a -> [a] -> [a]
inserta x xs = (takeWhile (<x) (xs)) ++ [x] ++ (dropWhile (<=x) (xs))
-- B) inserta directamente y versión recursiva
inserta' :: Ord a => a -> [a] -> [a]
inserta' x [ ] = [x]
inserta' x (y:ys) = [y] ++ inserta x (ys)
-- C)
p_inserta x xs = length xs <= 10 && desconocida xs ==> desconocida (inserta x xs)
p_inserta' x xs = length xs <= 10 && desconocida xs ==> desconocida (inserta' x xs)
pinserta = quickCheck (p_inserta :: Int -> [Int] -> Property)
pinserta' = quickCheck (p_inserta' :: Int -> [Int] -> Property)
-- D) ¿por qué está ordenada la lista 9 `inserta` (3 `inserta` (7 `inserta` []))
-- E) algoritmo de ordenación a través de foldr e inserta
ordena :: Ord a => [a] -> [a]
ordena xs = foldr inserta [] xs
16. Aunque la función predefinida lcm (least common multiple) ya lo hace, el objetivo de este ejercicio es escribir una función mcm para calcular el mínimo común múltiplo de dos naturales.
a) Define una función múltiplosDe, que tome como parámetro un número mayor que cero y
devuelva una lista infinita con sus múltiplos positivos. Por ejemplo:
múltiplosDe 3 [3,6,9,12,15,...
Ayuda: usa la función predefinida iterate, descrita en un ejercicio anterior.
b) Define la función sobrecargada para tipos con orden
primeroComún :: Ord a => [a] -> [a] -> a
que tome dos listas ordenadas ascendentemente y devuelva el menor elemento común a ambas.
Por ejemplo:
primeroComún [1,2,5,7,9] [3,3,4,5,7] 5
c) El mínimo común múltiplo de dos naturales x e y es el menor natural positivo múltiplo de
ambos. Utilizando las funciones definidas en los apartados previos, escribe una función mcm
que calcule el mínimo común múltiplo de dos naturales usando esta definición.
d) Comprueba usando QuickCheck tu definición mediante esta propiedad:
p_mcm x y = x>=0 && y>=0 ==> mcm x y == lcm x y
-- A)
múltiplosDe :: Integer -> [Integer]
múltiplosDe x = map (*x) [1,2..]
-- B)
primeroComún :: Ord a => [a] -> [a] -> a
primeroComún (x:xs) (y:ys)
| x > y = primeroComún (x:xs) ys
| x< y = primeroComún (xs) (y:ys)
|otherwise = x
-- C)
mcm de dos naturales como menor mútiplo común positivo
mcm x y = primeroComún (múltiplosDe x) (múltiplosDe y)
-- D)
p_mcm x y = x>=0 && y>=0 ==> mcm x y == lcm x y
Tema 3 - Examples/Ejercicios
1. Haskell. Escribe una función que permita determinar si una cadena de caracteres está bien
balanceada o equilibrada en lo que se refiere a los paréntesis, corchetes y llaves que contiene. El
resto de caracteres no interesan. Por ejemplo la cadena “v(hg(jij)hags{ss[dd]dd})” está
balanceada pero no así la cadena “ff(h([sds)sds]ss)hags”.
Para ello, se utilizará una pila en la que cada vez que aparezca un signo de apertura, se
introduce en la pila y cada vez que aparece un signo de cierre se extrae la cima de la pila y se
comprueba que corresponde al signo de apertura correspondiente. Si al finalizar de recorrer la
cadena la pila está vacía entonces la expresión está equilibrada.
module WellBalanced where
import DataStructures.Stack.LinearStack
wellBalanced :: String -> Bool
wellBalanced xs = wellBalanced' xs S.empty
wellBalanced' :: String -> Stack Char -> Bool
wellBalanced' [] s = isEmpty s
wellBalanced' (x:xs) s ...
*WellBalanced > wellBalanced "vv(hg(jij)hags{ss[dd]dd})"
True
module WellBalanced where
import DataStructures.Stack.LinearStack
wellBalanced :: String -> Bool
wellBalanced xs = wellBalanced' xs empty
wellBalanced' :: String -> Stack Char -> Bool
wellBalanced' [] s = True
wellBalanced' (x:xs) s
| isOpen x = wellBalanced' xs (push x s)
| isClosed x = if (match (top s) x) then wellBalanced' xs (pop s) else wellBalanced' (xs) s
| otherwise = wellBalanced' (xs) s
isOpen x = elem x "([{"
isClosed x = elem x ")]}"
match '(' ')' = True
match '[' ']' = True
match '{' '}' = True
match _ _ = False
11. Haskell. Un saco (o multiconjunto) es parecido a un conjunto salvo que un elemento puede estar
incluido varias veces. Por ejemplo, {‘b’, ‘a’, ‘d’, ‘d’, ‘a’, ‘c’ , b’, ‘a’} es un saco que
incluye tres ocurrencias del carácter ‘a’ , dos ocurrencias del ‘b’, una del ‘c’ y dos del ‘d’.
a) Implementa sacos en Haskell usando el siguiente tipo de datos:
data Bag a = Empty | Node a Int (Bag a)
de forma que en cada nodo aparece, además del saco restante, cada elemento junto con
su contador de ocurrencias, o sea, el número de veces que aparece. Para agilizar
las operaciones de inserción y borrado en un Bag, interesa que los nodos estén ordenados
atendiendo al orden de los elementos a incluir. Además, no deben aparecer elementos con
contador nulo (o negativo). Por ejemplo, el saco anterior se representa por:
Node ‘a’ 3 (Node ‘b’ 2 (Node ‘c’ 1 (Node ‘d’ 2 Empty)))
La implementación debe incluir las siguientes funciones:
empty :: Bag a -- Devuelve un saco vacío
isEmpty :: Bag a -> Bool -- Comprueba si un saco está vacío
insert :: (Ord a) => a -> Bag a -> Bag a --Inserta una nueva ocurrencia
occurrences :: (Ord a) => a -> Bag a -> Int -- Devuelve el número de
-- ocurrencias de un elemento en un saco (0 si el elemento no está) -}
delete :: (Ord a) => a -> Bag a -> Bag a -- Borra una ocurrencia de un
-- elemento de un saco. Devuelve el mismo saco si el elemento no estaba incluido
b) Proporciona una especificación de Bag definiendo sus axiomas para las diferentes
operaciones y comprueba la implementación realizada con QuickCheck. Para ello,
incluye en el módulo la siguiente instancia para generar sacos aleatorios:
instance (Ord a, Arbitrary a) => Arbitrary (Bag a) where arbitrary = do
xs <- listOf arbitrary return (foldr insert empty xs)
c) Añade al módulo las siguientes funciones para manipular sacos: unión, intersección,
diferencia y una función que determine si un saco está contenido en otro. Estas
funciones son semejantes a las de los conjuntos pero teniendo en cuenta las
ocurrencias de cada elemento.
d) Analiza la complejidad de las diferentes operaciones y justifica las ventajas de
mantener los elementos ordenados.
module Bag
( Bag
, empty
, isEmpty
, insert
, delete
, occurrences
) where
import Test.QuickCheck
----------------
--- a)
----------------
data Bag a = Empty | Node a Int (Bag a) deriving Eq --Node tiene 3 argumentos: a, n (entero) y la bolsa
-- no deben aparecer nodos con el contador a cero.
-- es decir, los eliminamos
-- mantenemos los objetos ordenados
-- imponemos una relación de orden al tipo base.
-- esto permitirá comparar dos sacos: igualdad, pertenencia, etc.
empty :: Bag a
empty = Empty
isEmpty :: Bag a -> Bool
isEmpty Empty = True
isEmpty (Node a n s) = False
--'a' -> Empty
--'c' -> Nodo 'a' 1 Empty
--'a' -> Nodo 'a' 1 (Nodo 'c' 1 Empty)
--'c' -> Nodo 'a' 1 (Nodo 'c' 2 Empty)
-- Nodo 'a' 2 (Nodo 'c' 2 Empty)
--Las bolsas se expresan: Nodo 'a' 3 (Nodo 'c' 5 (Nodo 'd' 1 Empty) = 'a' aparece 3 veces, 'c' 5 veces y 'd' 1 vez
-- (a 3):((c 5):((d 2) : [ ])) = ':' es nodo y [ ] es Empty
insert :: (Ord a) => a -> Bag a -> Bag a
-- Inserta una nueva ocurrencia en un saco
-- pedimos Ord para mantener el orden
insert x Empty = Node x 1 Empty
insert x (Node y oy s) | x < y = Node x 1 (Node y oy s)
| x == y = Node y (oy+1) s
| x > y = Node y oy (insert x s)
occurrences :: (Ord a) => a -> Bag a -> Int
-- Devuelve el número de ocurrencias de un elemento en un saco
-- 0 si el elemento no está
occurrences x Empty = 0
occurrences x (Node y oy s) | x ==y = oy
| x < y = 0
| x > y = occurrences x s
delete :: (Ord a) => a -> Bag a -> Bag a
-- Borra una ocurrencia de un elemento de un saco.
-- Devuelve el mismo saco si el elemento no estaba incluido
delete x Empty = Empty
delete x (Node y oy s) | x == y = Node y (oy-1) s
| x < y = Node y oy s
| x > y = Node y oy (delete x s)
instance (Show a) => Show (Bag a) where
show s = "Bag { " ++ show' s
where
show' Empty = "}"
show' (Node x ox s) = muestra x ox ++ show' s
muestra x 0 = ""
muestra x ox = show x ++ ' ':muestra x (ox-1)
----------------
--- b) Proporcionar una especificación de sacos definiendo sus axiomas
-- para las diferentes operaciones y comprobar la implementación
-- realizada con QuickCheck.
----------------
instance (Ord a, Arbitrary a) => Arbitrary (Bag a) where
arbitrary = do
xs <- listOf arbitrary
return (foldr insert empty xs)
-- Sobre insertar
ax1 x y s = insert x(insert y s) == insert y(insert x s)
-- Sobre ocurrences
ax2 x = empty
ax3 x y s | x==y = occurrences x (insert y s) == 1 + occurrences x s
| x/=y = occurrences x (insert y s) == occurrences x s
-- Sobre delete
ax4 x = empty
ax5 x s = delete x (insert x s) == s
ax6 x y s = x /= y ==> (delete x (insert y s) == insert y (delete x s))
-- Sobre isEmpty
ax7 = isEmpty empty
ax8 x s = isEmpty (insert x s) == False
{- Si no tipificamos, solo genera pruebas con 'data () = ()'
*Bag> quickCheck ax6
*** Gave up! Passed only 0 tests.
*Bag> quickCheck (ax6 :: Char -> Char -> Bag Char -> Property)
+++ OK, passed 100 tests.
*Bag> quickCheck (ax6 (12::Integer))
+++ OK, passed 100 tests.
*Bag> quickCheck (ax6 'a')
+++ OK, passed 100 tests.
*Bag> quickCheck (ax5 'a')
+++ OK, passed 100 tests.
*Bag> quickCheck (ax5 True)
+++ OK, passed 100 tests.
-}
----------------
-- c) Añadir al módulo las siguientes funciones para manipular sacos:
-- unión, intersección, diferencia y una función que determine si un saco
-- está contenido en otro.
-- Estas funciones son semejantes a las de conjunto pero teniendo
-- en cuenta las ocurrencias de cada elemento.
----------------
union :: Ord a => Bag a -> Bag a -> Bag a
-- queda curioso la unión al estilo de merge
union s Empty = s
union Empty t = t
union (Node x ox s) (Node y oy t)
|x<y = (Node x ox (Node y oy (union s t)))
|x>y = (Node y oy (Node x ox (union s t)))
|x==y = Node x (ox + oy) (union s t)
intersection :: Ord a => Bag a -> Bag a -> Bag a
intersection s Empty = Empty
intersection Empty t = Empty
intersection (Node x ox s) (Node y oy t)
| x == y = Node x (ox+oy) (intersection s t)
| x < y = intersection s (Node y oy t)
| x > y = intersection (Node x ox s) t
difference :: Ord a => Bag a -> Bag a -> Bag a
difference s Empty = s
difference Empty t = Empty
difference (Node x ox s) (Node y oy t)
| x == y = difference s t
| x < y = Node x ox (difference s (Node y oy t))
| x > y = difference (Node x ox s) t
inBag :: Ord a => Bag a -> Bag a -> Bool
inBag Empty t = True
inBag t Empty = False
inBag (Node x ox s) (Node y oy t)
| x==y = inBag s t
| x < y = False
| x > y = inBag (Node x ox s) t
-- Ejercicio: Encontrar axiomas para estas operaciones
-- union
ax_union1 s t = union s t == union t s
-- quickCheck (ax_union1 saco1)
-- union e insert
ax_union2 x s t = union (insert x s) t == union (insert x t) s
-- quickCheck (ax_union2 3)
-- intersection
ax_intersection1 s t = intersection s t == intersection t s
-- intersection y delete
ax_intersection2 x s t = occurrences x s == occurrences x t ==> intersection(delete x s) (delete x t) == intersection(delete x t) (delete x s)
--
-- difference y empty
ax_difference1 s = difference s s == Empty
-- quickCheck (ax_difference1 :: Set Integer -> Bool)
-- difference, insert y empty
ax_difference3 x s = difference s (insert x s) == Empty
-- quickCheck (ax_difference3 :: Int -> Bag Int -> Bool)
-- difference e insert
ax_difference4 x s t = difference t (insert x s) == difference (insert x t) s
-- quickCheck (ax_difference4 :: Int -> Bag Int -> Bag Int -> Bool)
-- otra propiedad de los conjuntos que es falsa para Bag
-- difference y union
ax_difference2 s t u = difference (union s t) u == union (difference s u) (difference t u)
-- quickCheck (ax_difference2 :: Bag Int -> Bag Int -> Bag Int -> Bool)
-- ATENCION: falla la distributiva
ax_uni_inter1 s t u = intersection (union s t) u ==
union (intersection s u) (intersection t u)
-- *Bag> quickCheck (ax_uni_inter1)
-- *** Failed! Falsifiable (after 4 tests):
-- Bag { () () }
-- Bag { () }
-- Bag { () }
------------
-- d) Analizad la complejidad de las diferentes operaciones
-- y justificar las ventajas de mantener los elementos ordenados.
------------
--- Otras funciones
cardinal :: Ord a => Bag a -> Int
cardinal Empty = 0
cardinal (Node x ox s) = ox + cardinal s
foldBag :: ( a -> Int -> b -> b) -> b -> Bag a -> b
foldBag f z Empty = z
foldBag f z (Node x ox s) = f x ox (foldBag f z s)
elemBag y = foldBag esta False
where esta x ox b = x==y && b
sumBag = foldBag sum3 0
where sum3 x ox z = x*(fromIntegral ox) + z
cardinal' = foldBag (const (+) ) 0
-- Las ventajas de tener los elementos ordenados es que permite que se puede operar
--con las bolsas de una forma más rápida y además las funciones necesitan menos casos base.
--------------------------------------------------------------------------------
Demas ejercicios de las relaciones de otros años
--------------------------------------------------------------------------------
1. Consideremos la función:
dosVeces :: (Integer -> Integer) -> Integer -> Integer
dos Veces f x = f (f x)
a) ¿Cuál es el tipo de la función: fun = dosVeces (+1)?
fun :: Integer -> Integer
b) Escribe una λ-expresión equivalente a la función fun
fun’ :: Integer -> Integer
fun’ = λx -> x + 2
c) Escribe una sección equivalente a la función fun
fun'’ :: Integer -> Integer
fun’’ = (+2)
2. Escribe una función derivada que devuelva la derivada de una función de reales en reales
usando la aproximación:
Por ejemplo:
coseno :: Float -> Float
coseno= derivada sin
MAIN> derivada sqrt 1.0
> 0.499487 :: Float
MAIN> coseno 0.0
> 1.0 :: Float
¿Cuál es el tipo de la función derivada?
derivada :: (Float -> Float) -> Float -> Float
derivada f x = (f (x + epsilon) – f x) / épsilon
where
epsilon = 1e - 4
3. ¿Cuáles son los tipos polimórficos de las siguientes funciones?
swap (x, y) = (y, x) (a,b) -> (b,a)
const x y = x a -> b -> a
subst f g x = f x (g x) (a -> b -> c) -> (a -> b) -> a -> c
pair (f,g) x = (f x, g x) (a -> b, a -> c) -> a -> (b, c)
cross (f,g) (x,y) = (f x, g y) (a -> b, c -> d) -> (a, c) -> (b, d)
comp f g x = f (g x) (a -> b) -> (c -> a) -> c -> b
fix f = f (fix f) (a -> a) -> a
4. ¿Qué hace el siguiente operador?
infixr 0 >$>
(>$>) :: (a -> a) -> a -> a
f >$> x = f (f x)
Aplica la función dos veces
¿Por qué su tipo no es (>$>) :: (a -> b) -> a -> b?
La función f debe devolver un parámetro del mismo tipo que su parámetro de entrada
porque el resultado de aplicar la primera vez la función será el parámetro de entrada
en la segunda aplicación de la función
5. Define el operador (>$>) del ejercicio anterior usando la composición de funciones (.)
infixr 0 >$> (>$>) :: (a->a) -> a -> a (>$>) f = f . f
6. Consideremos el siguiente operador:
infixl 9 >.>f >.> g = λ x -> g (f x)
¿Cuál es su tipo polimórfico)
(a -> b) -> (b -> c) -> a -> c
¿Qué hace la siguiente función?
fun :: Integer -> Integer
fun = (+2) >.> (*2) >.> (+1)
Debido a la asociación a la izquierda del operador, sería como tener ((+2) >.>
(*2)) >.> (+1). Dado un número natural, primero le suma 2, luego lo multiplica por 2, y
luego le suma 1.
7. La función predefinida reverse invierte el orden de los elementos de una lista. Por ejemplo
MAIN> reverse [1,2,3]
[3,2,1] :: Integer
Defínela y da su tipo polimórfico. ¿Cuál es el tipo de la función palíndromo xs = (reverse xs ==
xs)?
reverse :: [a] -> [a]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
palíndromo :: [a] -> Bool
palíndromo xs = (reverse xs == xs)
8. ¿Cuál es el tipo polimórfico de la función twice?
twice f x = f (f x)
twice :: (a -> a) -> a -> a
¿Cuál es el valor de cada una de las siguintes expresiones?
twice (+1) 0 2
twice twice (+1) 0 4
Demostración:
twice (+1) 0 -- f = (+1) ; x = 0
=> definición de twice
(+1) ( (+1) 0 )
=> definición de la función (+1) (la más interna, pues es la única aplicable)
(+1) 1
=> definición de la función (+1)
2
twice twice (+1) 0 -- f = twice (+1) ; x = 0
=> definición de twice (en el rédex más externo)
twice (+1) (twice (+1) 0)
=> definición de twice (en el twice más interno)
twice (+1) ( (+1) ( (+1) 0 ))
=> definición de la función (+1) (la más interna, pues es la única aplicable)
twice (+1) ( (+1) 1 )
=> definición de la función (+1) (la más interna, pues es la única aplicable)
twice (+1) 2
=> definición de la función twice
(+1) ( (+1) 2 )
=> definición de la función (+1) (la más interna, pues es la única aplicable)
(+1) 3
=> definición de la función (+1)
4
9 La función predefinida zip empareja dos listas. Por ejemplo
? zip [1, 2, 3] [4, 5, 6]
[(1, 4), (2, 5), (3, 6)] :: [(Integer , Integer )]
? zip [1, 2, 3] [True, False]
[(1,True), (2, False)] :: [(Integer ,Bool )]
? zip [True, False] [1, 2, 3]
[(True, 1), (False, 2)] :: [(Bool , Integer )]
Defínela y da su tipo polimórfico
zip :: [a] -> [b] -> [(a,b)]
zip' (x:xs) (y:ys) = (x,y) : zip' xs ys
zip' _ _ = []
10. ¿Cuál es el tipo de las siguientes expresiones (en caso de que sean correctas)?
not . even Int -> Bool (Bool -> Bool) -> (Int -> Bool)
even . not Incorrecto, ya que la función not produce un Booleano y
la función even necesita como entrada un número
chr . ord Char -> Char (Int -> Char) -> (Char -> Int)
ord . chr Int -> Int (Char -> Int) -> (Int -> Char)
ord . chr . (+1) Int -> Int (Char -> Int) -> ( (Int -> Char) -> (Int -> Int) )
map not [Bool] -> [Bool]
map (λ x -> not x ) [Bool] -> [Bool]
map (not . even) [Int] -> [Bool]
map not [True, False] [Bool]
map ord [Char] -> [Int]
map (+1) [a] -> [a], donde a es un número
map (map (+1)) [[a]] -> [[a]], donde a es un número
map (++[1]) [[a]] -> [[a]]
map (1 :) [[a]] -> [[a]]
Cuál es el valor de la expresión map (map (+1)) [[1, 2, 3], [10, 11]]?
[[2,3,4], [10,11]]
map (map (+1)) ([1,2,3] : [[10,11]]) = map (+1) [1,2,3] : map (map (+1)) [[10,11]]
Primera parte:
map (+1) [1,2,3] = [2,3,4]
Segunda parte:
map (map (+1)) ([10,11] : []) = map (+1) [10,11] : map (map (+1)) []
Primera parte:
map (+1) [10,11] = [11,12]
Segunda parte:
map (map (+1)) [] = []
Uniendo todo, resulta:
[2,3,4] : [11.12] : [] = [[2,3,4], [11,12]]
balanceada o equilibrada en lo que se refiere a los paréntesis, corchetes y llaves que contiene. El
resto de caracteres no interesan. Por ejemplo la cadena “v(hg(jij)hags{ss[dd]dd})” está
balanceada pero no así la cadena “ff(h([sds)sds]ss)hags”.
Para ello, se utilizará una pila en la que cada vez que aparezca un signo de apertura, se
introduce en la pila y cada vez que aparece un signo de cierre se extrae la cima de la pila y se
comprueba que corresponde al signo de apertura correspondiente. Si al finalizar de recorrer la
cadena la pila está vacía entonces la expresión está equilibrada.
module WellBalanced where
import DataStructures.Stack.LinearStack
wellBalanced :: String -> Bool
wellBalanced xs = wellBalanced' xs S.empty
wellBalanced' :: String -> Stack Char -> Bool
wellBalanced' [] s = isEmpty s
wellBalanced' (x:xs) s ...
*WellBalanced > wellBalanced "vv(hg(jij)hags{ss[dd]dd})"
True
module WellBalanced where
import DataStructures.Stack.LinearStack
wellBalanced :: String -> Bool
wellBalanced xs = wellBalanced' xs empty
wellBalanced' :: String -> Stack Char -> Bool
wellBalanced' [] s = True
wellBalanced' (x:xs) s
| isOpen x = wellBalanced' xs (push x s)
| isClosed x = if (match (top s) x) then wellBalanced' xs (pop s) else wellBalanced' (xs) s
| otherwise = wellBalanced' (xs) s
isOpen x = elem x "([{"
isClosed x = elem x ")]}"
match '(' ')' = True
match '[' ']' = True
match '{' '}' = True
match _ _ = False
11. Haskell. Un saco (o multiconjunto) es parecido a un conjunto salvo que un elemento puede estar
incluido varias veces. Por ejemplo, {‘b’, ‘a’, ‘d’, ‘d’, ‘a’, ‘c’ , b’, ‘a’} es un saco que
incluye tres ocurrencias del carácter ‘a’ , dos ocurrencias del ‘b’, una del ‘c’ y dos del ‘d’.
a) Implementa sacos en Haskell usando el siguiente tipo de datos:
data Bag a = Empty | Node a Int (Bag a)
de forma que en cada nodo aparece, además del saco restante, cada elemento junto con
su contador de ocurrencias, o sea, el número de veces que aparece. Para agilizar
las operaciones de inserción y borrado en un Bag, interesa que los nodos estén ordenados
atendiendo al orden de los elementos a incluir. Además, no deben aparecer elementos con
contador nulo (o negativo). Por ejemplo, el saco anterior se representa por:
Node ‘a’ 3 (Node ‘b’ 2 (Node ‘c’ 1 (Node ‘d’ 2 Empty)))
La implementación debe incluir las siguientes funciones:
empty :: Bag a -- Devuelve un saco vacío
isEmpty :: Bag a -> Bool -- Comprueba si un saco está vacío
insert :: (Ord a) => a -> Bag a -> Bag a --Inserta una nueva ocurrencia
occurrences :: (Ord a) => a -> Bag a -> Int -- Devuelve el número de
-- ocurrencias de un elemento en un saco (0 si el elemento no está) -}
delete :: (Ord a) => a -> Bag a -> Bag a -- Borra una ocurrencia de un
-- elemento de un saco. Devuelve el mismo saco si el elemento no estaba incluido
b) Proporciona una especificación de Bag definiendo sus axiomas para las diferentes
operaciones y comprueba la implementación realizada con QuickCheck. Para ello,
incluye en el módulo la siguiente instancia para generar sacos aleatorios:
instance (Ord a, Arbitrary a) => Arbitrary (Bag a) where arbitrary = do
xs <- listOf arbitrary return (foldr insert empty xs)
c) Añade al módulo las siguientes funciones para manipular sacos: unión, intersección,
diferencia y una función que determine si un saco está contenido en otro. Estas
funciones son semejantes a las de los conjuntos pero teniendo en cuenta las
ocurrencias de cada elemento.
d) Analiza la complejidad de las diferentes operaciones y justifica las ventajas de
mantener los elementos ordenados.
module Bag
( Bag
, empty
, isEmpty
, insert
, delete
, occurrences
) where
import Test.QuickCheck
----------------
--- a)
----------------
data Bag a = Empty | Node a Int (Bag a) deriving Eq --Node tiene 3 argumentos: a, n (entero) y la bolsa
-- no deben aparecer nodos con el contador a cero.
-- es decir, los eliminamos
-- mantenemos los objetos ordenados
-- imponemos una relación de orden al tipo base.
-- esto permitirá comparar dos sacos: igualdad, pertenencia, etc.
empty :: Bag a
empty = Empty
isEmpty :: Bag a -> Bool
isEmpty Empty = True
isEmpty (Node a n s) = False
--'a' -> Empty
--'c' -> Nodo 'a' 1 Empty
--'a' -> Nodo 'a' 1 (Nodo 'c' 1 Empty)
--'c' -> Nodo 'a' 1 (Nodo 'c' 2 Empty)
-- Nodo 'a' 2 (Nodo 'c' 2 Empty)
--Las bolsas se expresan: Nodo 'a' 3 (Nodo 'c' 5 (Nodo 'd' 1 Empty) = 'a' aparece 3 veces, 'c' 5 veces y 'd' 1 vez
-- (a 3):((c 5):((d 2) : [ ])) = ':' es nodo y [ ] es Empty
insert :: (Ord a) => a -> Bag a -> Bag a
-- Inserta una nueva ocurrencia en un saco
-- pedimos Ord para mantener el orden
insert x Empty = Node x 1 Empty
insert x (Node y oy s) | x < y = Node x 1 (Node y oy s)
| x == y = Node y (oy+1) s
| x > y = Node y oy (insert x s)
occurrences :: (Ord a) => a -> Bag a -> Int
-- Devuelve el número de ocurrencias de un elemento en un saco
-- 0 si el elemento no está
occurrences x Empty = 0
occurrences x (Node y oy s) | x ==y = oy
| x < y = 0
| x > y = occurrences x s
delete :: (Ord a) => a -> Bag a -> Bag a
-- Borra una ocurrencia de un elemento de un saco.
-- Devuelve el mismo saco si el elemento no estaba incluido
delete x Empty = Empty
delete x (Node y oy s) | x == y = Node y (oy-1) s
| x < y = Node y oy s
| x > y = Node y oy (delete x s)
instance (Show a) => Show (Bag a) where
show s = "Bag { " ++ show' s
where
show' Empty = "}"
show' (Node x ox s) = muestra x ox ++ show' s
muestra x 0 = ""
muestra x ox = show x ++ ' ':muestra x (ox-1)
----------------
--- b) Proporcionar una especificación de sacos definiendo sus axiomas
-- para las diferentes operaciones y comprobar la implementación
-- realizada con QuickCheck.
----------------
instance (Ord a, Arbitrary a) => Arbitrary (Bag a) where
arbitrary = do
xs <- listOf arbitrary
return (foldr insert empty xs)
-- Sobre insertar
ax1 x y s = insert x(insert y s) == insert y(insert x s)
-- Sobre ocurrences
ax2 x = empty
ax3 x y s | x==y = occurrences x (insert y s) == 1 + occurrences x s
| x/=y = occurrences x (insert y s) == occurrences x s
-- Sobre delete
ax4 x = empty
ax5 x s = delete x (insert x s) == s
ax6 x y s = x /= y ==> (delete x (insert y s) == insert y (delete x s))
-- Sobre isEmpty
ax7 = isEmpty empty
ax8 x s = isEmpty (insert x s) == False
{- Si no tipificamos, solo genera pruebas con 'data () = ()'
*Bag> quickCheck ax6
*** Gave up! Passed only 0 tests.
*Bag> quickCheck (ax6 :: Char -> Char -> Bag Char -> Property)
+++ OK, passed 100 tests.
*Bag> quickCheck (ax6 (12::Integer))
+++ OK, passed 100 tests.
*Bag> quickCheck (ax6 'a')
+++ OK, passed 100 tests.
*Bag> quickCheck (ax5 'a')
+++ OK, passed 100 tests.
*Bag> quickCheck (ax5 True)
+++ OK, passed 100 tests.
-}
----------------
-- c) Añadir al módulo las siguientes funciones para manipular sacos:
-- unión, intersección, diferencia y una función que determine si un saco
-- está contenido en otro.
-- Estas funciones son semejantes a las de conjunto pero teniendo
-- en cuenta las ocurrencias de cada elemento.
----------------
union :: Ord a => Bag a -> Bag a -> Bag a
-- queda curioso la unión al estilo de merge
union s Empty = s
union Empty t = t
union (Node x ox s) (Node y oy t)
|x<y = (Node x ox (Node y oy (union s t)))
|x>y = (Node y oy (Node x ox (union s t)))
|x==y = Node x (ox + oy) (union s t)
intersection :: Ord a => Bag a -> Bag a -> Bag a
intersection s Empty = Empty
intersection Empty t = Empty
intersection (Node x ox s) (Node y oy t)
| x == y = Node x (ox+oy) (intersection s t)
| x < y = intersection s (Node y oy t)
| x > y = intersection (Node x ox s) t
difference :: Ord a => Bag a -> Bag a -> Bag a
difference s Empty = s
difference Empty t = Empty
difference (Node x ox s) (Node y oy t)
| x == y = difference s t
| x < y = Node x ox (difference s (Node y oy t))
| x > y = difference (Node x ox s) t
inBag :: Ord a => Bag a -> Bag a -> Bool
inBag Empty t = True
inBag t Empty = False
inBag (Node x ox s) (Node y oy t)
| x==y = inBag s t
| x < y = False
| x > y = inBag (Node x ox s) t
-- Ejercicio: Encontrar axiomas para estas operaciones
-- union
ax_union1 s t = union s t == union t s
-- quickCheck (ax_union1 saco1)
-- union e insert
ax_union2 x s t = union (insert x s) t == union (insert x t) s
-- quickCheck (ax_union2 3)
-- intersection
ax_intersection1 s t = intersection s t == intersection t s
-- intersection y delete
ax_intersection2 x s t = occurrences x s == occurrences x t ==> intersection(delete x s) (delete x t) == intersection(delete x t) (delete x s)
--
-- difference y empty
ax_difference1 s = difference s s == Empty
-- quickCheck (ax_difference1 :: Set Integer -> Bool)
-- difference, insert y empty
ax_difference3 x s = difference s (insert x s) == Empty
-- quickCheck (ax_difference3 :: Int -> Bag Int -> Bool)
-- difference e insert
ax_difference4 x s t = difference t (insert x s) == difference (insert x t) s
-- quickCheck (ax_difference4 :: Int -> Bag Int -> Bag Int -> Bool)
-- otra propiedad de los conjuntos que es falsa para Bag
-- difference y union
ax_difference2 s t u = difference (union s t) u == union (difference s u) (difference t u)
-- quickCheck (ax_difference2 :: Bag Int -> Bag Int -> Bag Int -> Bool)
-- ATENCION: falla la distributiva
ax_uni_inter1 s t u = intersection (union s t) u ==
union (intersection s u) (intersection t u)
-- *Bag> quickCheck (ax_uni_inter1)
-- *** Failed! Falsifiable (after 4 tests):
-- Bag { () () }
-- Bag { () }
-- Bag { () }
------------
-- d) Analizad la complejidad de las diferentes operaciones
-- y justificar las ventajas de mantener los elementos ordenados.
------------
--- Otras funciones
cardinal :: Ord a => Bag a -> Int
cardinal Empty = 0
cardinal (Node x ox s) = ox + cardinal s
foldBag :: ( a -> Int -> b -> b) -> b -> Bag a -> b
foldBag f z Empty = z
foldBag f z (Node x ox s) = f x ox (foldBag f z s)
elemBag y = foldBag esta False
where esta x ox b = x==y && b
sumBag = foldBag sum3 0
where sum3 x ox z = x*(fromIntegral ox) + z
cardinal' = foldBag (const (+) ) 0
-- Las ventajas de tener los elementos ordenados es que permite que se puede operar
--con las bolsas de una forma más rápida y además las funciones necesitan menos casos base.
--------------------------------------------------------------------------------
Demas ejercicios de las relaciones de otros años
--------------------------------------------------------------------------------
1. Consideremos la función:
dosVeces :: (Integer -> Integer) -> Integer -> Integer
dos Veces f x = f (f x)
a) ¿Cuál es el tipo de la función: fun = dosVeces (+1)?
fun :: Integer -> Integer
b) Escribe una λ-expresión equivalente a la función fun
fun’ :: Integer -> Integer
fun’ = λx -> x + 2
c) Escribe una sección equivalente a la función fun
fun'’ :: Integer -> Integer
fun’’ = (+2)
2. Escribe una función derivada que devuelva la derivada de una función de reales en reales
usando la aproximación:
Por ejemplo:
coseno :: Float -> Float
coseno= derivada sin
MAIN> derivada sqrt 1.0
> 0.499487 :: Float
MAIN> coseno 0.0
> 1.0 :: Float
¿Cuál es el tipo de la función derivada?
derivada :: (Float -> Float) -> Float -> Float
derivada f x = (f (x + epsilon) – f x) / épsilon
where
epsilon = 1e - 4
3. ¿Cuáles son los tipos polimórficos de las siguientes funciones?
swap (x, y) = (y, x) (a,b) -> (b,a)
const x y = x a -> b -> a
subst f g x = f x (g x) (a -> b -> c) -> (a -> b) -> a -> c
pair (f,g) x = (f x, g x) (a -> b, a -> c) -> a -> (b, c)
cross (f,g) (x,y) = (f x, g y) (a -> b, c -> d) -> (a, c) -> (b, d)
comp f g x = f (g x) (a -> b) -> (c -> a) -> c -> b
fix f = f (fix f) (a -> a) -> a
4. ¿Qué hace el siguiente operador?
infixr 0 >$>
(>$>) :: (a -> a) -> a -> a
f >$> x = f (f x)
Aplica la función dos veces
¿Por qué su tipo no es (>$>) :: (a -> b) -> a -> b?
La función f debe devolver un parámetro del mismo tipo que su parámetro de entrada
porque el resultado de aplicar la primera vez la función será el parámetro de entrada
en la segunda aplicación de la función
5. Define el operador (>$>) del ejercicio anterior usando la composición de funciones (.)
infixr 0 >$> (>$>) :: (a->a) -> a -> a (>$>) f = f . f
6. Consideremos el siguiente operador:
infixl 9 >.>f >.> g = λ x -> g (f x)
¿Cuál es su tipo polimórfico)
(a -> b) -> (b -> c) -> a -> c
¿Qué hace la siguiente función?
fun :: Integer -> Integer
fun = (+2) >.> (*2) >.> (+1)
Debido a la asociación a la izquierda del operador, sería como tener ((+2) >.>
(*2)) >.> (+1). Dado un número natural, primero le suma 2, luego lo multiplica por 2, y
luego le suma 1.
7. La función predefinida reverse invierte el orden de los elementos de una lista. Por ejemplo
MAIN> reverse [1,2,3]
[3,2,1] :: Integer
Defínela y da su tipo polimórfico. ¿Cuál es el tipo de la función palíndromo xs = (reverse xs ==
xs)?
reverse :: [a] -> [a]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
palíndromo :: [a] -> Bool
palíndromo xs = (reverse xs == xs)
8. ¿Cuál es el tipo polimórfico de la función twice?
twice f x = f (f x)
twice :: (a -> a) -> a -> a
¿Cuál es el valor de cada una de las siguintes expresiones?
twice (+1) 0 2
twice twice (+1) 0 4
Demostración:
twice (+1) 0 -- f = (+1) ; x = 0
=> definición de twice
(+1) ( (+1) 0 )
=> definición de la función (+1) (la más interna, pues es la única aplicable)
(+1) 1
=> definición de la función (+1)
2
twice twice (+1) 0 -- f = twice (+1) ; x = 0
=> definición de twice (en el rédex más externo)
twice (+1) (twice (+1) 0)
=> definición de twice (en el twice más interno)
twice (+1) ( (+1) ( (+1) 0 ))
=> definición de la función (+1) (la más interna, pues es la única aplicable)
twice (+1) ( (+1) 1 )
=> definición de la función (+1) (la más interna, pues es la única aplicable)
twice (+1) 2
=> definición de la función twice
(+1) ( (+1) 2 )
=> definición de la función (+1) (la más interna, pues es la única aplicable)
(+1) 3
=> definición de la función (+1)
4
9 La función predefinida zip empareja dos listas. Por ejemplo
? zip [1, 2, 3] [4, 5, 6]
[(1, 4), (2, 5), (3, 6)] :: [(Integer , Integer )]
? zip [1, 2, 3] [True, False]
[(1,True), (2, False)] :: [(Integer ,Bool )]
? zip [True, False] [1, 2, 3]
[(True, 1), (False, 2)] :: [(Bool , Integer )]
Defínela y da su tipo polimórfico
zip :: [a] -> [b] -> [(a,b)]
zip' (x:xs) (y:ys) = (x,y) : zip' xs ys
zip' _ _ = []
10. ¿Cuál es el tipo de las siguientes expresiones (en caso de que sean correctas)?
not . even Int -> Bool (Bool -> Bool) -> (Int -> Bool)
even . not Incorrecto, ya que la función not produce un Booleano y
la función even necesita como entrada un número
chr . ord Char -> Char (Int -> Char) -> (Char -> Int)
ord . chr Int -> Int (Char -> Int) -> (Int -> Char)
ord . chr . (+1) Int -> Int (Char -> Int) -> ( (Int -> Char) -> (Int -> Int) )
map not [Bool] -> [Bool]
map (λ x -> not x ) [Bool] -> [Bool]
map (not . even) [Int] -> [Bool]
map not [True, False] [Bool]
map ord [Char] -> [Int]
map (+1) [a] -> [a], donde a es un número
map (map (+1)) [[a]] -> [[a]], donde a es un número
map (++[1]) [[a]] -> [[a]]
map (1 :) [[a]] -> [[a]]
Cuál es el valor de la expresión map (map (+1)) [[1, 2, 3], [10, 11]]?
[[2,3,4], [10,11]]
map (map (+1)) ([1,2,3] : [[10,11]]) = map (+1) [1,2,3] : map (map (+1)) [[10,11]]
Primera parte:
map (+1) [1,2,3] = [2,3,4]
Segunda parte:
map (map (+1)) ([10,11] : []) = map (+1) [10,11] : map (map (+1)) []
Primera parte:
map (+1) [10,11] = [11,12]
Segunda parte:
map (map (+1)) [] = []
Uniendo todo, resulta:
[2,3,4] : [11.12] : [] = [[2,3,4], [11,12]]
Tema 4 - Teoria General
ARBOL NORMAL
Creacion de un Arbol normal:
tree1= Node 1[ Node 2 [ Node 4 [],Node 5[], Node 6[]], Node 3 [ Node 7 [] ] ]
suma de los valores de los nodos
sumT tree1
altura de un arbol
heighT tree1
ARBOL BINARIO
creacion de un arbol binario
tree2 =NodeB 1 (NodeB 2 (NodeB 4 EmptyB EmptyB) (NodeB 5 EmptyB EmptyB))
(NodeB 3 (NodeB 6 EmptyB EmptyB) EmptyB)
Suma:
sumB tree2
saber que hay a que nivel del arbol binario
atLevelB 0 tree2
caminos hasta cierto nodo desde la raiz
pathsToB (nodo) (arbol)
pathsToB 5 tree2
preOrderB tree2
inOrderB tree2
postOrderB tree2
construir un arbol binario de altura minima
minTreeB' :: [a] -> TreeB a
minTreeB' xs = fst (aux (length xs) xs)
aux :: Int -> [a] -> (TreeB a, [a])
aux 0 xs = (EmptyB, xs)
aux 1 xs = (NodeB (head xs) EmptyB EmptyB, tail xs)
aux n xs = (NodeB y t1 t2, zs)
where
m = div n 2
(t1, y:ys) = aux m xs
(t2, zs) = aux (n-m-1) ys
COLAS CON PRIORIDAD (SIFO)
definir una cola
cola1 =enqueue 3 (enqueue 1 (enqueue 2 (empty)))
devuelve una cola vacía
empty :: PQueue a
test para colas vacías
isEmpty :: PQueue a -> Bool
inserta un elemento en una cola de prioridad
enqueue :: (Ord a) => a -> PQueue a -> PQueue a
devuelve el elemento de mínima prioridad
first :: (Ord a) => PQueue a -> a
devuelve la cola obtenida al eliminar el elemento de mínima prioridad
dequeue :: (Ord a) => PQueue a -> PQueue a
MONTICULO - HEAP ORDER PROPERTY EN JAVA
public BinaryHeap()
public int size()
public boolean isEmpty()
private boolean lessThan(int idx1, int idx2)
private void swap(int idx1, int idx2)
private boolean isRoot(int idx)
private int parent(int idx)
private int leftChild(int idx)
private int rightChild(int idx)
private boolean isNode(int idx)
private boolean hasLeftChild(int idx)
private boolean isLeaf(int idx)
public void insert(T x)
public T minElem()
ARBOLES BINARIOS DE BUSQUEDA - BINARY SEARCH TREE (BST) HASKELL
busqueda:
search 5 tree2
es elemento:
isElem 5 tree2
isElem x t = isJust (search x t)
existe una funcion llamada Maybe, que si encuentra SOLO algo entonces asigna el valor TRUE
data Maybe a = Nothing | Just a
isJust :: Maybe a -> Bool
-- si contiene algo entonces da true
isJust (Just _) = True
--si no contiene nada da false
isJust Nothing = False
Minimo elemento
minim tree2
Maximo elemento
maxim tree2
Quitar y devolver el mínimo elemento de un árbol:
split tree2
Eliminar
delete 4 tree2
Unir
join tree1 tree2
ARBOLES BINARIOS DE BUSQUEDA - BINARY SEARCH TREE (BST) JAVA
public boolean isEmpty();
public int size();
public int height();
public void insert(K k, V v);
public V search(K k);
public boolean isElem(K k);
public void delete(K k);
public Iterable<K> inOrder();
public Iterable<K> postOrder();
public Iterable<K> preOrder();
public Iterable<V> values();
public Iterable<Tuple2<K,V>> keysValues();
ARBOLES AVL
son arboles que como mucho difieren en altura 1
AVL treeAVL = Nodo 9 (Nodo 3 (Nodo 2 Hoja Hoja) (Nodo 4 Hoja Hoja)) (Nodo 7 Hoja Hoja)
height treeAVL
isAVL treeAVL
rotR treeAVL
rotL treeAVL
insert 4 treeAVL
inclinacion a la izquierda ,derecha y balanceando de nuevo
rightLeaning treeAVL
leftLeaning treeAVL
balance altura parteIZQ parteDER
busqueda
search 5 treeAVL
isElem 5 treeAVL
delete 5 treeAVL
join treeAVL1 treeAVL2
elimina y devuelve el minimo elemento de un arbol
split treeAVL
ARBOLES AVL EN JAVA
public Tree(C key, D value)
isEmpty();
height(Tree<?,?> tree);
rightLeaning();
leftLeaning();
setHeight();
rotR();
rotL();
balanced();
search(K key);
isElem(K key);
DICCIONARIOS HASKELL
diccionario claves valores
diccionario = empty
isEmpty diccionario
insert clave valor diccionario
valueof valor diccionario
Creacion de un Arbol normal:
tree1= Node 1[ Node 2 [ Node 4 [],Node 5[], Node 6[]], Node 3 [ Node 7 [] ] ]
suma de los valores de los nodos
sumT tree1
altura de un arbol
heighT tree1
ARBOL BINARIO
creacion de un arbol binario
tree2 =NodeB 1 (NodeB 2 (NodeB 4 EmptyB EmptyB) (NodeB 5 EmptyB EmptyB))
(NodeB 3 (NodeB 6 EmptyB EmptyB) EmptyB)
Suma:
sumB tree2
saber que hay a que nivel del arbol binario
atLevelB 0 tree2
caminos hasta cierto nodo desde la raiz
pathsToB (nodo) (arbol)
pathsToB 5 tree2
preOrderB tree2
inOrderB tree2
postOrderB tree2
construir un arbol binario de altura minima
minTreeB' :: [a] -> TreeB a
minTreeB' xs = fst (aux (length xs) xs)
aux :: Int -> [a] -> (TreeB a, [a])
aux 0 xs = (EmptyB, xs)
aux 1 xs = (NodeB (head xs) EmptyB EmptyB, tail xs)
aux n xs = (NodeB y t1 t2, zs)
where
m = div n 2
(t1, y:ys) = aux m xs
(t2, zs) = aux (n-m-1) ys
COLAS CON PRIORIDAD (SIFO)
definir una cola
cola1 =enqueue 3 (enqueue 1 (enqueue 2 (empty)))
devuelve una cola vacía
empty :: PQueue a
test para colas vacías
isEmpty :: PQueue a -> Bool
inserta un elemento en una cola de prioridad
enqueue :: (Ord a) => a -> PQueue a -> PQueue a
devuelve el elemento de mínima prioridad
first :: (Ord a) => PQueue a -> a
devuelve la cola obtenida al eliminar el elemento de mínima prioridad
dequeue :: (Ord a) => PQueue a -> PQueue a
MONTICULO - HEAP ORDER PROPERTY EN JAVA
public BinaryHeap()
public int size()
public boolean isEmpty()
private boolean lessThan(int idx1, int idx2)
private void swap(int idx1, int idx2)
private boolean isRoot(int idx)
private int parent(int idx)
private int leftChild(int idx)
private int rightChild(int idx)
private boolean isNode(int idx)
private boolean hasLeftChild(int idx)
private boolean isLeaf(int idx)
public void insert(T x)
public T minElem()
ARBOLES BINARIOS DE BUSQUEDA - BINARY SEARCH TREE (BST) HASKELL
busqueda:
search 5 tree2
es elemento:
isElem 5 tree2
isElem x t = isJust (search x t)
existe una funcion llamada Maybe, que si encuentra SOLO algo entonces asigna el valor TRUE
data Maybe a = Nothing | Just a
isJust :: Maybe a -> Bool
-- si contiene algo entonces da true
isJust (Just _) = True
--si no contiene nada da false
isJust Nothing = False
Minimo elemento
minim tree2
Maximo elemento
maxim tree2
Quitar y devolver el mínimo elemento de un árbol:
split tree2
Eliminar
delete 4 tree2
Unir
join tree1 tree2
ARBOLES BINARIOS DE BUSQUEDA - BINARY SEARCH TREE (BST) JAVA
public boolean isEmpty();
public int size();
public int height();
public void insert(K k, V v);
public V search(K k);
public boolean isElem(K k);
public void delete(K k);
public Iterable<K> inOrder();
public Iterable<K> postOrder();
public Iterable<K> preOrder();
public Iterable<V> values();
public Iterable<Tuple2<K,V>> keysValues();
ARBOLES AVL
son arboles que como mucho difieren en altura 1
AVL treeAVL = Nodo 9 (Nodo 3 (Nodo 2 Hoja Hoja) (Nodo 4 Hoja Hoja)) (Nodo 7 Hoja Hoja)
height treeAVL
isAVL treeAVL
rotR treeAVL
rotL treeAVL
insert 4 treeAVL
inclinacion a la izquierda ,derecha y balanceando de nuevo
rightLeaning treeAVL
leftLeaning treeAVL
balance altura parteIZQ parteDER
busqueda
search 5 treeAVL
isElem 5 treeAVL
delete 5 treeAVL
join treeAVL1 treeAVL2
elimina y devuelve el minimo elemento de un arbol
split treeAVL
ARBOLES AVL EN JAVA
public Tree(C key, D value)
isEmpty();
height(Tree<?,?> tree);
rightLeaning();
leftLeaning();
setHeight();
rotR();
rotL();
balanced();
search(K key);
isElem(K key);
DICCIONARIOS HASKELL
diccionario claves valores
diccionario = empty
isEmpty diccionario
insert clave valor diccionario
valueof valor diccionario
Tema 4 - Examples/Ejercicios
La biblioteca de Haskell (Haskell.zip) debe descomprimirse en un directorio y después desde WinGHCi hacer referencia a ella a través de la orden:
Por ejemplo, si descomprimes Haskell.zip en el directorio de trabajo donde están la plantilla y el programa de prueba, la orden anterior es:Prelude> :set -idirectorioDondeSeEncuentraLaBiblioteca
EJERCICIO MONTICULOS MAXIFOBICOS HASKELL
Este ejercicio está tomado de http://programmingpraxis.com.
Los montículos Maxifóbicos (Maxophobics) son una variante de montículos zurdos, y ambos
permiten una implementación eficiente de una cola de prioridad.
En los zurdos, el peso del hijo izquierdo es siempre mayor o igual que el del derecho.
Como los zurdos, los maxifóbicos se representan por medio de árboles binarios aumentados, que
mantienen la propiedad de Heap Order, y por tanto, en cada nodo la clave es menor que las claves
de sus dos hijos. Por tanto el minElem se encuentra en la raíz del árbol. delMin descarta la raíz y
mezcla sus dos hijos e insert mezcla el árbol existente con un nuevo árbol con el elemento a
insertar como único elemento (singleton).
Los árboles zurdos son simples de utilizar y de codificar; es fácil demostrar que la mezcla de dos
montículos zurdos (resuelto en las transparencias) es zurdo, y de esta forma la longitud de su espina
derecha no supera al logaritmo del total de nodos de la mezcla, siendo ésta, salvo un factor de
proporcionalidad, una cota del número de operaciones de la mezcla. De aquí se deduce que las
operaciones de inserción y eliminación tienen complejidad logarítmica, ya que éstas se reducen a
una mezcla.
Los montículos maxifóbicos son una alternativa a los zurdos, pero no necesitan estar balanceados a
la izquierda (lo que libera del uso del invariante de los zurdos), y sin embargo las operaciones de
inserción y borrado tienen complejidad logarítmica. La diferencia de los montículos zurdos con los
maxifóbicos se encuentra en la operación de mezclar. En los maxifóbicos, la operación de mezcla se
implementa comparando los valores de las raíces de los dos árboles. La menor se mantiene como
valor de la raíz del montículo hm combinado resultado de la mezcla; el resto de la información se
distribuye en tres árboles: el montículo ganador de la comparación (que contendrá la clave mayor),
y los hijos del perdedor. De estos tres árboles se toma el mayor (con más nodos) y se coloca en la
rama derecha del montículo mezcla hm, colocándose en la rama izquierda la mezcla de los dos
restantes. De esta forma el montículo combinado contiene todas las claves de los originales y
además verifica la propiedad HO (heap order). Sorprendentemente un sencillo razonamiento
permite demostrar que esta mezcla tiene complejidad logarítmica (véase el artículo de Chris
Okasaki, “Alternatives to Two Classic Data Structures”, SIGCSE’05, February 23–27, 2005).
El nombre maxifóbico quiere decir “esquivando (o descartando) el mayor”: se mezclan los dos
menores subárboles.
a) Completa la implementación de las operaciones de estos montículos maxifóbicos, modificando lo
necesario en el código de los árboles zurdos (en DataStructures.Heap.MaxiphobicHeap).
b) (Difícil) La operación de mezcla de montículos maxifóbicos normalmente se implementa con
complejidad logarítmica por lo que es una implementación eficiente. Probar que efectivamente la
operación tiene complejidad logarítmica.
Ayuda: Sea T(N) el número de invocaciones a la función de mezcla para mezclar dos árboles
maxifóbicos cuyo número total de nodos de los dos subárboles es N. Intenta escribir una relación
de recurrencia para T(N) teniendo en cuenta que el mayor de los subárboles se ha evitado en cada
paso. Para calcular el tamaño total de los dos subárboles que se mezclarán (los dos más pequeños),
se debería primeramente determinar cuantos elementos tiene el subárbol evitado (el mayor).
c) Si interpretamos el tamaño (size) de un montículo maxifóbico como la altura del árbol, modifica el
código y estudia la complejidad de la operación de mezcla.
module MaxiphobicHeap
( Heap
, empty
, isEmpty
, minElem
, delMin
, insert -- puede definirse via merge
, merge
-- los siguientes son auxiliares
, mkHeap
-- , isHeap -- si exportamos leftChild, rightChild puede ser auxiliar
-- , verifyOP -- si exportamos leftChild, rightChild puede ser auxiliar
-- , drawOnWith
) where
-- import DrawTrees
import Test.QuickCheck
data Heap a = Empty | Node a Int (Heap a) (Heap a) deriving Show
-- number of elements
size :: Heap a -> Int
size Empty = 0
size (Node _ sz _ _) = sz
empty :: Heap a
empty = Empty
isEmpty :: Heap a -> Bool
isEmpty Empty = True
isEmpty _ = False
singleton :: a -> Heap a
singleton x = Node x 1 Empty Empty
insert :: (Ord a) => a -> Heap a -> Heap a
insert x h = merge (singleton x) h
minElem :: Heap a -> a
minElem Empty = error "minElem on empty heap"
minElem (Node x _ _ _) = x
delMin :: (Ord a) => Heap a -> Heap a
delMin Empty = error "delMin on empty heap"
delMin (Node _ _ lh rh) = merge lh rh
----------------------------------------------------------
---------------------------------------------------------
-- recursively merges smallest subheaps. Achieves O(log n) complexity
merge :: (Ord a) => Heap a -> Heap a -> Heap a
merge Empty h' = h'
merge h Empty = h
merge h@(Node x sz lh rh) h'@(Node x' sz' lh' rh')
| x < x' = Node x (sz+sz') smaller1 smaller2
| otherwise = merge h' h
where
smaller1
| ((size lh <= size rh)&&(size lh <= sz')) = lh
| ((size rh <= size lh)&&(size rh <= sz')) = rh
| otherwise = h'
smaller2
| ((size lh <= size rh)&&(size lh <= sz')) = merge rh h'
| ((size rh <= size lh)&&(size rh <= sz')) = merge lh h'
| otherwise = merge lh rh
----------------------------------------------------------
----------------------------------------------------------
-- Efficient O(n) bottom-up construction for heaps
mkHeap :: (Ord a) => [a] -> Heap a
mkHeap [] = empty
mkHeap xs = mergeLoop (map singleton xs)
where
mergeLoop [h] = h
mergeLoop hs = mergeLoop (mergePairs hs)
mergePairs [] = []
mergePairs [h] = [h]
mergePairs (h:h':hs) = merge h h' : mergePairs hs
{-
-------------------------------------------------------------------------------
-- Generating arbitrary Heaps
-------------------------------------------------------------------------------
instance (Ord a, Arbitrary a) => Arbitrary (Heap a) where
arbitrary = do
xs <- arbitrary
return (mkHeap xs)
-------------------------------------------------------------------------------
-- Invariants
-------------------------------------------------------------------------------
verifyOP :: (Ord a) => Heap a -> Bool
verifyOP Empty = True
verifyOP (Node x _ lh rh) = x `lessEq` lh && x `lessEq` rh
&& verifyOP lh && verifyOP rh
where
x `lessEq` Empty = True
x `lessEq` (Node x' _ _ _) = x<=x'
-------------------------------------------------------------------------------
-- Drawing a Heap
-------------------------------------------------------------------------------
instance Subtrees (Heap a) where
subtrees Empty = []
subtrees (Node _ _ lh rh) = [lh,rh]
instance (Show a) => ShowNode (Heap a) where
showNode (Node x _ _ _) = show x
drawOnWith :: FilePath -> (a -> String) -> Heap a -> IO ()
drawOnWith file toString = _drawOnWith file showHeap
where
showHeap (Node x _ _ _) = toString x
-}
----------------------------------------------------------------------------------------
------------------------------------para probar--------------------------------------
----------------------------------------------------------------------------------------
module MaxiphobicHeapDemos where
import Data.List(nub)
import MaxiphobicHeap
import DataStructures.Random
import DrawTrees
drawHeap :: (Show a) => Heap a -> IO ()
drawHeap = drawOn "MaxiphobicHeap.png"
outlineHeap :: Heap a -> IO ()
outlineHeap = outlineOn "MaxiphobicHeap.png"
drawCharHeap :: String -> IO ()
drawCharHeap xs = drawOnWith "MaxiphobicHeap.png" (\k -> [k]) (mkHeap xs)
drawIntHeap :: Heap Int -> IO ()
drawIntHeap h = drawOnWith "MaxiphobicHeap.png" (\k -> show k) h
randomHeap :: Int -> Seed -> Heap Int
randomHeap sz seed = mkHeap (take sz . nub . randoms $ seed)
randomHeapI (a,b) sz seed = mkHeap (take sz . nub . randomsR (a,b) $ seed)
-- take 10 $ randomsR (1,10) 32
demo1 sz seed = outlineHeap (randomHeap sz seed)
demoI (a,b) sz seed = drawIntHeap (randomHeapI (a,b) sz seed)
demo2 xs = drawHeap (mkHeap xs)
demo3 = drawCharHeap "murcielago"
{-
h1 = foldl (flip insert) empty [4,2,3,1,2,4,7,1]
instance (Ord a, Arbitrary a) => Arbitrary (Heap a) where
arbitrary = do
vs <- arbitrary
return (foldr insert empty vs)
isSorted :: (Ord a) => [a] -> Bool
isSorted [] = True
isSorted [x] = True
isSorted (x:y:zs) = x<=y && isSorted (y:zs)
p1 xs = isSorted ys && null (xs\\ys) && null(ys\\xs)
where
h = toHeap xs
ys = toList h
toHeap xs = foldr insert empty xs
toList h
| isEmpty h = []
| otherwise = minElem h : toList (delMin h)
-}
MISMO EJERCICIO EN JAVA
Implementa los montículos maxifóbicos en Java.
(dataStructures.heap.MaxiphobicHeap.java)
/**
* Binary Search trees implementation
*/
package tree;
import java.util.Iterator;
import java.util.NoSuchElementException;
import stack.Stack;
import stack.StackLink;
public class BST<K extends Comparable<? super K>, V> implements Iterable<K> { protected static class Tree<K, V> { protected K key; protected V value; protected Tree<K, V> left; protected Tree<K, V> right;
public Tree(K k, V v) { key = k; value = v; left = null; right = null; } }
private Tree<K, V> root;
public BST() { root = null; }
public boolean isEmpty() { return root == null; }
public void insert(K k, V v) { root = BST.insertRec(root, k, v); }
// returns modified tree private static <K extends Comparable<? super K>,V> Tree<K, V> insertRec(Tree<K, V> node, K key, V value) { if (node == null) node = new Tree<K, V>(key, value); else if (key.compareTo(node.key) == 0) node.value = value; else if (key.compareTo(node.key) < 0) node.left = insertRec(node.left, key, value); else node.right = insertRec(node.right, key, value); return node; }
public V search(K key) { return BST.searchRec(root, key); }
private static <K extends Comparable<? super K>,V> V searchRec(Tree<K, V> tree, K key) { if (tree == null) return null; else if (key.compareTo(tree.key) == 0) return tree.value; else if (key.compareTo(tree.key) < 0) return searchRec(tree.left, key); else return searchRec(tree.right, key); }
public boolean isElem(K key) { return search(key) != null; }
// pre: node is a non-empty tree // Removes minimum key (and value) from tree rooted at node. Before // deletion, key and value are saved into temp node. // returns modified tree (without min key and value) private static <K,V> Tree<K, V> split(Tree<K, V> node, Tree<K, V> temp) { if (node.left == null) { // min node found, so copy min key and value temp.key = node.key; temp.value = node.value; return node.right; // remove node } else { // remove min from left subtree node.left = split(node.left, temp); return node; } }
public void delete(K key) { root = BST.deleteRec(root, key); }
// returns modified tree private static <K extends Comparable<? super K>,V> Tree<K, V> deleteRec(Tree<K, V> node, K key) { if (node == null) ; // key not found; do nothing else if (key.compareTo(node.key) == 0) { if (node.left == null) node = node.right; else if (node.right == null) node = node.left; else /* * Tuple3<K,V,Tree<K,V>> tuple = split(node.right); node.key = * tuple._1(); node.value = tuple._2(); node.right = tuple._3(); */ node.right = split(node.right, node); } else if (key.compareTo(node.key) < 0) node.left = deleteRec(node.left, key); else node.right = deleteRec(node.right, key); return node; } // iterators
private abstract class Traversal implements Iterator<K> { Stack<K> stack = new StackLink<K>();
abstract void save(Tree<K, V> node);
public Traversal() { if (root != null) save(root); }
public boolean hasNext() { return !stack.isEmpty(); }
public K next() { if (!hasNext()) throw new NoSuchElementException();
K either = stack.top(); stack.pop(); return either; }
public void remove() { throw new UnsupportedOperationException(); }
}
public Iterator<K> inOrder() { return new Traversal() { void save(Tree<K, V> node) { // in reverse order, cause stack is LIFO if (node.right != null) save(node.right); stack.push(node.key); if (node.left != null) save(node.left); } }; }
public Iterator<K> postOrder() { return new Traversal() { void save(Tree<K, V> node) { // in reverse order, cause stack is LIFO stack.push(node.key); if (node.right != null) save(node.right); if (node.left != null) save(node.left); } }; }
public Iterator<K> preOrder() { return new Traversal() { void save(Tree<K, V> node) { // in reverse order, cause stack is LIFO if (node.right != null) save(node.right); if (node.left != null) save(node.left); stack.push(node.key); } }; }
public Iterator<K> iterator() { return inOrder(); }
}
BST
data TreeB a = EmptyB | NodeB a (TreeB a) (TreeB a) deriving Show
preOrderB :: TreeB a -> [a]
preOrderB EmptyB = []
preOrderB (NodeB x lt rt) = [x] ++ preOrderB lt ++ preOrderB rt
inOrderB :: TreeB a -> [a]
inOrderB EmptyB = []
inOrderB (NodeB x lt rt) = inOrderB lt ++ [x] ++ inOrderB rt
postOrderB :: TreeB a -> [a]
postOrderB EmptyB = []
postOrderB (NodeB x lt rt) = postOrderB lt ++ postOrderB rt ++ [x]
-- crea un arbol binario conocido su recorrido en preorden y enorden
-- findTreeB [2,1,3,4,6] [3,1,4,2,6]
-- ----> NodeB 2 (NodeB 1 (NodeB 3 EmptyB EmptyB) (NodeB 4 EmptyB EmptyB)) (NodeB 6 EmptyB EmptyB)
findTreeB :: Eq a => [a] -> [a] -> TreeB a
findTreeB [] _ = EmptyB
findTreeB (x:xs) ys = NodeB x (findTreeB is' is) (findTreeB ds' ds)
where
(is,ds) = span (/=x) ys --is es la rama izquierda y ds es la rama derecha
nts = length is
(is',ds') = splitAt nts xs
--span :: (a->Bool)->[a] -> ([a],[a]) Mientras se cumpla el predicado, va entrando elementos en la lista
--splitAt :: Int -> [a] -> ([a],[a]) Parte la lista
----------------------------------------------
JAVA
------------------------------------------------
/**
* Binary Search trees implementation
*/
package tree;
import java.util.Iterator;
import java.util.NoSuchElementException;
import stack.Stack;
import stack.StackLink;
public class BST<K extends Comparable<? super K>, V> implements Iterable<K> {
protected static class Tree<K, V> {
protected K key;
protected V value;
protected Tree<K, V> left;
protected Tree<K, V> right;
public Tree(K k, V v) {
key = k;
value = v;
left = null;
right = null;
}
}
private Tree<K, V> root;
public BST() {
root = null;
}
public boolean isEmpty() {
return root == null;
}
public void insert(K k, V v) {
root = BST.insertRec(root, k, v);
}
// returns modified tree
private static <K extends Comparable<? super K>,V> Tree<K, V> insertRec(Tree<K, V> node, K key, V value) {
if (node == null)
node = new Tree<K, V>(key, value);
else if (key.compareTo(node.key) == 0)
node.value = value;
else if (key.compareTo(node.key) < 0)
node.left = insertRec(node.left, key, value);
else
node.right = insertRec(node.right, key, value);
return node;
}
public V search(K key) {
return BST.searchRec(root, key);
}
private static <K extends Comparable<? super K>,V> V searchRec(Tree<K, V> tree, K key) {
if (tree == null)
return null;
else if (key.compareTo(tree.key) == 0)
return tree.value;
else if (key.compareTo(tree.key) < 0)
return searchRec(tree.left, key);
else
return searchRec(tree.right, key);
}
public boolean isElem(K key) {
return search(key) != null;
}
// pre: node is a non-empty tree
// Removes minimum key (and value) from tree rooted at node. Before
// deletion, key and value are saved into temp node.
// returns modified tree (without min key and value)
private static <K,V> Tree<K, V> split(Tree<K, V> node, Tree<K, V> temp) {
if (node.left == null) {
// min node found, so copy min key and value
temp.key = node.key;
temp.value = node.value;
return node.right; // remove node
} else {
// remove min from left subtree
node.left = split(node.left, temp);
return node;
}
}
public void delete(K key) {
root = BST.deleteRec(root, key);
}
// returns modified tree
private static <K extends Comparable<? super K>,V> Tree<K, V> deleteRec(Tree<K, V> node, K key) {
if (node == null)
; // key not found; do nothing
else if (key.compareTo(node.key) == 0) {
if (node.left == null)
node = node.right;
else if (node.right == null)
node = node.left;
else
/*
* Tuple3<K,V,Tree<K,V>> tuple = split(node.right); node.key =
* tuple._1(); node.value = tuple._2(); node.right = tuple._3();
*/
node.right = split(node.right, node);
} else if (key.compareTo(node.key) < 0)
node.left = deleteRec(node.left, key);
else
node.right = deleteRec(node.right, key);
return node;
}
// iterators
private abstract class Traversal implements Iterator<K> {
Stack<Either<K, Tree<K, V>>> stack = new StackLink<Either<K, Tree<K, V>>>();
abstract void save(Tree<K, V> node);
public Traversal() {
if (root != null)
save(root);
}
public boolean hasNext() {
return !stack.isEmpty();
}
public K next() {
if (!hasNext())
throw new NoSuchElementException();
Either<K, Tree<K, V>> either = stack.top();
stack.pop();
while (either.isRight()) {
Tree<K, V> node = either.right();
save(node);
either = stack.top();
stack.pop();
}
return either.left();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
public Iterator<K> inOrder() {
return new Traversal() {
void save(Tree<K, V> node) {
// in reverse order, cause stack is LIFO
if (node.right != null)
stack.push(new Right<K, Tree<K, V>>(node.right));
stack.push(new Left<K, Tree<K, V>>(node.key));
if (node.left != null)
stack.push(new Right<K, Tree<K, V>>(node.left));
}
};
}
public Iterator<K> postOrder() {
return new Traversal() {
void save(Tree<K, V> node) {
// in reverse order, cause stack is LIFO
stack.push(new Left<K, Tree<K, V>>(node.key));
if (node.right != null)
stack.push(new Right<K, Tree<K, V>>(node.right));
if (node.left != null)
stack.push(new Right<K, Tree<K, V>>(node.left));
}
};
}
public Iterator<K> preOrder() {
return new Traversal() {
void save(Tree<K, V> node) {
// in reverse order, cause stack is LIFO
if (node.right != null)
stack.push(new Right<K, Tree<K, V>>(node.right));
if (node.left != null)
stack.push(new Right<K, Tree<K, V>>(node.left));
stack.push(new Left<K, Tree<K, V>>(node.key));
}
};
}
public Iterator<K> iterator() {
return inOrder();
}
}
Suscribirse a:
Entradas (Atom)