Tabla de Contenidos
Wiki de Scripts Haskell
Esta página Wiki es editable por cualquiera. La idea es poner pedazos de código Haskell con los programas que vayamos haciendo o nos parezcan interesantes.
A manera de inicio contribuyo con un Haskell script que contiene algunas definiciones del Capítulo 8 de “Extracto del Cálculo de Programas”
Programa -- 8.2 Definiciones -- El numero pi esta definido en Hugs.Prelude.pi, o sea en el "preludio" --pi = 3.141592657 cuadrado x = x * x area r = pi * cuadrado r -- Expresiones ... hay que evaluarlas -- 8.3 Reglas para el calculo con definiciones -- Composicion de funciones hyperCubo x = x * x * x * x hyperCubo' x = (cuadrado.cuadrado) x -- Notacion infija hyperCubo'' x = ((.) cuadrado cuadrado) x -- 8.4 Definiciones locales raiz1 a b c = (-b - sqrt disc) / (2*a) where disc = cuadrado b - 4*a*c -- 8.5 Analisis por casos max a b | a<=b = b | b<=a = a fac n | n==0 = 1 | n/=0 = n*fac (n-1) n b | b = 1 | not b = 0 -- 8.6 Pattern Matching fac' 0 = 1 fac' (n+1) = (n+1) * fac' n -- 8.7 Tipos, 8.8 Tipos basicos -- Booleano verdad :: Bool verdad = True || (False && not True) -- Entero respuesta :: Int respuesta = 42 -- Caracter inicial :: Char inicial = 'I' -- Cadena de caracteres materia :: String materia = "Introduccion a los Algoritmos" -- Funcion sumar :: Int -> Int -> Int sumar x y = x+y -- Funcion polimorfica id :: a -> a id x = x -- 8.9 Tuplas sumRat :: (Int,Int) -> (Int,Int) -> (Int,Int) sumRat (a,b) (c,d) = (a*d+b*c , b*d) -- 8.10 Listas notas :: [Int] notas = [4, 7, 8, 4, 2, 5, 7] alumnos :: [String] alumnos = ["Cabanillas", "Egea", "Berlin", "Tamburrini", "Rodriguez", "Biasi", "Ferme"] rara :: [Int] rara = length alumnos : notas!!0 : notas ++ [2,2,2]
Ejercicios capítulo 8 del Extracto del Cálculo de Programas
Soluciones de Ejercicio 8.7 (edad)
'Pablo Zurita'
Mi solución es similar a las anteriores con la diferencia que chequea la validez de los datos y le da un mensaje de error útil al usuario en caso de dar algun dato incorrecto.
edad :: (Int, Int, Int) -> (Int, Int, Int) -> Int edad (d, m, a) (d', m', a') | 12 < m || 12 < m' || m < 1 || m' < 1 = error "El mes no es valido." | ([31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] !!(m - 1)) < d = error "El dia del mes inicial no es valido." | ([31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] !!(m' - 1)) < d' = error "El dia del mes final no es valido." | a < 0 || a' < a = error "El año inicial es menor que cero o menor que el año final." | a == a' && m' < m = error "Mismo año pero el mes inicial es mayor que el final." | a == a' && m' == m && d' < d = error "Mismo mes y año pero dia inicial mayor que el final." | (m' < m) || (m' == m && d' < d) = (a'-a)-1 -- No alcanza a completar un año. | otherwise = a'-a -- Ningun caso especial, resta directa.
'Nico Visintin'
edad :: (Int, Int, Int) -> (Int, Int, Int) -> Int edad (d,m,a) (d',m',a') |m'<m = a'-a-1 |m'>m = a'-a |m'==m && d'>=d = a'-a |m'==m && d'<d = a'-a-1
'Pedro A.'
Pues yo propongo dos soluciones, iguales pero a su vez distintas. La única diferencia es que en una solamente contemplo un caso, ya que si no ocurre ese caso, pues, ocurre el caso contrario. Y en la segunda, pues analizo dos casos, la diferencia es que no posee la estructura del if. Ambas poseen una combinación de operadores lógicos.
edad' :: (Int,Int,Int) -> (Int,Int,Int) -> Int edad' (d,m,y) (d',m',y') = if m < m' || (m == m' && d <= d') then y' - y else y' - y - 1
edad :: (Int,Int,Int) -> (Int,Int,Int) -> Int edad (d,m,y) (d',m',y') | (m < m' || (m == m' && d <= d')) = y' - y | (m > m' || (m == m' && d > d')) = y' - y - 1
(José Neder) Este es mas lindo a la vista:
edad :: (Int, Int, Int) → (Int, Int, Int) → Int edad (x,y,z) (o,p,q) | y<p||(y==p&&x<o) = q-z
| otherwise = q-z-1
Soluciones de Ejercicio 8.8 (área del prisma rectangular)
Usando definiciones locales y calculando bien el área de los laterales (N. Wolovick)
area :: Int -> Int -> Int -> Int area h b d = 2*frente + 2*lado + 2*arriba where frente = h*b lado = h*d arriba = b*d
'Nico Visintin'
area :: (Int, Int, Int) -> Int area (h,b,d) |h >=0 && b >=0 && d>=0 = 2*h*b + 2*h*d + 2*b*d area' :: (Int, Int, Int) -> Int area' (h,b,d) = 2*h*b + 2*h*d + 2*b*d
Cierto… Habia calculado mal el area de cada lado…
Soluciones del Práctico 8
cabeza :: [Int] -> Int cabeza [a] = a cabeza (x:xs) = x
Creeria que esta bien, saludos a todos
(nwolovick) Fijate que el primer caso es redundante con el segundo, pues [a]=a:[], luego se puede eliminar sin problemas.
Vinsanity
Ejercicio 6
Hasta 10000
pip :: Int -> Bool pip n = mod n 7==0 || mod n 10==7 || div (mod n 100) 10==7 || div (mod n 1000) 100==7 || div n 1000==7
(nwolovick) Fijate que podés ganar legibilidad usando definiciones locales y notación infija:pip' :: Int -> Bool pip' n = n `mod` 7==0 || unidades==7 || decenas==7 || centenas==7 || unidadesDeMil==7 where unidades = n `mod` 10 decenas = (n `div` 10) `mod` 10 centenas = (n `div` 100) `mod` 10 unidadesDeMil = (n `div` 1000) `mod` 10(PabloZ) Hay que validar la información y en caso que sea un valor no valido hay que darle al usuario un mensaje de error que pueda entender.pip :: Int -> Bool pip n | n < 0 = error "Valor no valido, usar cualquier natural." | n >= 10000 = error "Valor no valido, usar naturales menores que 10000." | mod n 7 == 0 || unidades == 7 || decenas == 7 || centenas == 7 || miles == 7 = True | otherwise = False where unidades = n `mod` 10 decenas = (n `div` 10) `mod` 10 centenas = (n `div` 100) `mod` 10 miles = (n `div` 1000) `mod` 10Y las probé usando
Main> map pip [0..9999] == map pip' [0..9999] True
Teoricamente para cualquier numero.
pip2 :: Int -> Bool pip2 n = mod n 7==0 || digall 7 (digi n) where digall :: Int -> [Int] -> Bool digall n [x] = x==n digall n (x:xs) = x==n || digall n xs digi :: Int -> [Int] digi x | x<10 && x>=0 = [x] | x>10 = digi (div x 10) ++ [mod x 10]
José Neder
¿Asi estara mal? Primero defini unmil, centena, decena y unidad
unmil :: Int -> Int unmil x | 999<x && x<10000 = x`div`1000 | otherwise = 0 centena :: Int -> Int centena x | 999<x && x<10000 = (x`mod`1000)`div`100 | 99<x && x<1000 = x`div`100 | otherwise = 0
decena :: Int → Int
decena x | 999<x && x<10000 = ((x`mod`1000)`mod`100)`div`10 | 99<x && x<1000 = (x`mod`100)`div`10 | 9<x && x<100 = x`div`10 | otherwise = 0
unidad :: Int -> Int unidad x | 999<x && x<10000 = ((x`mod`1000)`mod`100)`mod`10 | 99<x && x<1000 = (x`mod`100)`mod`10 | 9<x && x<100 = x`mod`10 | 0<=x && x<10 = x
pip :: Int → Bool
pip x | x`mod`7 == 0 = True | (unmil x) == 7 || (centena x) == 7 || (decena x) == 7 || (unidad x) ==7 = True | otherwise = False
Ejercicio 7
cabeza :: [Int] -> Int cabeza [a] = a cabeza (x:xs) = x
ahora si, disculpen :p
Vinsanity
(José Neder) No hace falta aclarar ¨cabeza [a] = a¨ ya que [a] = a:[]
cabeza :: [a] -> a cabeza (x:xs) = x
cola :: [a] -> [a] cola (x:xs) = xs
ó
cabeza :: [a] -> a cabeza (x:_) = x
cola :: [a] -> [a] cola (_:xs) = xs
(nwolovick) Esto es usando el “Comodín” o variable anónima_
.(PabloZ) Hay que validar la información y en caso que sea un valor no valido hay que darle al usuario un mensaje de error que pueda entender.cabeza :: [a] -> a cabeza [] = error "La lista no puede estar vacia." cabeza (x:xs) = xcola :: [a] -> [a] cola a | length a == 0 = error "La lista no puede estar vacia." cola (x:xs) = xs
Ejercicio 8
-Creeria que es asi
2*cuadrado.(cabeza.[2,4,5,6,7,8])
2*cuadrado.(cabeza.(2:[4,5,6,7,8]))
2*cuadrado.2
2*(2*2)
2*4
8
José Neder
Ejercicio 9
Todo excepto el ultimo elemento
inicio :: [a] -> [a] inicio [x] = [] inicio (x:xs) = x : inicio xs
El ultimo elemento
ultimo :: [a] -> a ultimo [x] = x ultimo (x:xs) = ultimo xs
José Neder
====== Yo lo pense de esta forma no se si estara bien…. :S
inicio :: [a] -> [a] inicio(x:xs) = reverse ( cola ( reverse (x:xs)))
lo que hice es dar vuelta la lista tomar la cola -que ya estaba definida-( osea todo menos el primer elemento, que es el ultimo de la lista original) y aplicarle reverse para que vuelva a su estado normal… jeje…
ultimo :: [a] -> a ultimo (x:xs) = cabeza (reverse (x:xs))
como cabeza ya estaba definida y reverse esta en el prelude… Andrés… ahh me olvide poner un msj de error si la lista es vacia… seria asi en los dos casos
inicio/ultimo [] = error "lista vacia" ======
Ejercicio 10
Duplica todos los enteros de una lista de enteros
duplicar :: [Int] -> [Int] duplicar [] = [] duplicar (x:xs) = x*2 : duplicar xs
Si se quiere en racionales(no periodicos)
duplicar :: [Float] -> [Float] duplicar [] = [] duplicar (x:xs) = x*2 : duplicar xs
tambien se puede hacer general a todos los tipos de numeros:
duplicar :: Num a => [a] -> [a] duplicar [] = [] duplicar (x:xs) = x*2 : duplicar xs
José Neder
Ejercicio 11
Esto va aca
Anaconda Express
multiplicar :: [Int] -> Int -> [Int] multiplicar [] a = [] multiplicar (x:xs) a = x*a : multiplicar xs a
Igual pero para racionales
multiplicar :: [Float] -> Float -> [Float] multiplicar [] a = [] multiplicar (x:xs) a = x*a : multiplicar xs a
y general
multiplicar :: Num a => [a] -> a -> [a] multiplicar [] a = [] multiplicar (x:xs) a = x*a : multiplicar xs a
José Neder
Ejercicio 12
'Walter Alini'
Acá se me ocurre usar una función de longitud de lista propia, pero que devuelva un Float en vez de un Int, para después poder usarla en la definición de promedio (esto es porque no existe una función de conversión -aka casteo- de Int a Float):
longitud :: [A] -> Float longitud [] = 0 longitud (x: xs) = 1 + longitud xs
Ahora sí podemos hacer la función multiplicar, que tendrá la siguiente signatura (la definición queda como ejercicio para el lector);
promedio :: [Float] -> Float
Otra idea sería hacer nuestra propia función que pase de Int a Float:
int2Float :: Int -> Float int2Float = fromIntegral
Entonces, podemos usar, cada vez que necesitemos la longitud de una lista (pero como un Float):
int2Float(length xs)
en vez de:
(length xs)
sumatoria :: Num a => [a] -> a sumatoria [] = 0 sumatoria (x:xs) = x + sumatoria xs
cardinal :: Num a => [a] -> a cardinal [] = 0 cardinal (x:xs) = 1 + cardinal xs
promedio :: [Float] -> Float promedio [] = 0 promedio xs = (sumatoria xs)/(cardinal xs)
José Neder
Ejercicio 13
reversa :: [a] -> [a] reversa [] = [] reversa (x:xs) = reversa xs ++ [x]
Suponiendo que concatenar(++) no esta definido, lo defino como
conca :: [a] -> [a] -> [a] conca [] ys = ys conca (x:xs) ys = x : (conca xs ys)
reversa1 :: [a] -> [a] reversa1 [] = [] reversa1 (x:xs) = conca (reversa1 xs) [x]
José Neder
Ejercicio 14
listcap :: Eq a => [a] -> Bool listcap xs = xs==(reversa xs)
o
listcap2 :: Eq a => [a] -> String listcap2 xs | xs==(reversa xs) = "Es capicua" | otherwise = "No es capicua"
Esto es porque “The problem here is that in order to compare lists for equality one can only do this when the elements themselves carry an equality function. This is necessary even when comparing with the empty list” que en castellano es algo como esto: “El problema aqui es que, uno puede comparar la equivalencia de las listas solo cuando los elementos en si mismos tienen una funcion de equivalencia. Esto es necesario aun cuando se compara con una lista vacia”
José Neder
(nwolovick) Perfecto, necesitamos que la variable de tipoa
acepte la igualdad, por eso ponemosEq a ⇒ …
al principio.
(nwolovick) ¿Quién da una definición recursiva pura?
ya la dijiste asi que la pongo
listcap3 [a] -> Bool listcap3 [] = True listcap3 [x] = True listcap3 (x:xs) = x==ultimo xs && listcap (inicio xs)
Ejercicio 15
Algo asi:
reversa.[2,4,5,6,7,8] reversa.[4,5,6,7,8] ++ [2] (reversa.[5,6,7,8] ++ [4]) ++ [2] ((reversa.[6,7,8] ++ [5]) ++ [4]) ++ [2] (((reversa.[7,8] ++ [6]) ++ [5]) ++ [4]) ++ [2] ((((reversa.[8] ++ [7]) ++ [6]) ++ [5]) ++ [4]) ++ [2] (((((reversa.[] ++ [8]) ++ [7]) ++ [6]) ++ [5]) ++ [4]) ++ [2] ((((([] ++ [8]) ++ [7]) ++ [6]) ++ [5]) ++ [4]) ++ [2] (((([8] ++ [7]) ++ [6]) ++ [5]) ++ [4]) ++ [2] ((([8,7] ++ [6]) ++ [5]) ++ [4]) ++ [2] (([8,7,6] ++ [5]) ++ [4]) ++ [2] ([8,7,6,5] ++ [4]) ++ [2] [8,7,6,5,4] ++ [2] [8,7,6,5,4,2]
José Neder
Ejercicio 16
Esto va aca
desdeHasta :: Int -> Int -> [Int] desdeHasta n m | n>m = reverse (desdeHasta m n) | n==m = [n] | n<m = n : desdeHasta (n+1) m
Hay una solución que no utiliza reverse y que para n>m usa el mismo patrón que para n<m (N.Wolovick)
desdeHasta :: Int -> Int -> [Int] desdeHasta n m | n > m = n : desdeHasta (n-1) m | n == m = [n] | n < m = n : desdeHasta (n+1) m
Puede ser esa una solución?. (Pedro A.)De peluche, mejorale la “indentación” asi quedan bien encolumnadas las guardas y las definiciones
desdehasta :: Int -> Int -> [Int] desdehasta a b |a<b = [a..b] |a==b = [a] |a>b = reversa [b..a]
Tambien lo hice yo desde esta forma. (PATRICK R.)
desde :: Int -> Int -> [Int] desde x y |x > y = x:desde (x-1) y |x < y = x:desde (x+1) y |otherwise = [y]
Ejercicio 17
listbin :: [Int] -> Bool listbin [] = False listbin [y] = y==0||y==1 listbin (x:xs) = (x==0||x==1)&&listbin xs
José Neder
(nwolovick) fijate que como [y]=y:[] el primer caso esta de mas, mmmhhh, pero ahora que lo miro lo hicistes para parchar un poco el caso base que es incorrecto, el caso base esTrue
porque sabemos el teorema del neutro de la conjunción. (José Neder) desde mi punto de vista una lista vacia no esta compuesta solo de ceros o de unos, sino que podemos creer que no esta compuesta de nada o que esta compuesta de todo.
Solucion mas elegante
listabinaria :: [Int] -> Bool listabinaria [] = False -- Una lista vacia no tiene ni ceros ni unos. listabinaria (x:xs) | (x == 0 || x == 1) && xs == [] = True -- x es el ultimo elemento, si es 0 o 1 ya devolvemos. | (x == 0 || x == 1) = True && listabinaria (xs) -- El elemento actual es 0 o 1, vamos al siguiente. | otherwise = False -- El elemento actual no es ni 0 ni 1. Ya es falso.
Pablo Zurita.
En esta solucion se ve si los num de la lista son numeros binarios de mas de una cifra….
listbin' :: [Int] -> Bool listbin' [] = False listbin' [y] = bin (dig y) listbin' (x:xs) = bin (dig x) && listbin' xs
bin :: [Int] -> Bool bin [x] = (x==0 || x==1) bin (x:xs) = (x==0 || x==1) && bin xs dig :: Int -> [Int] dig x | x>=0 && x<10 = [x] | x>= 10 = dig (div x 10) ++ [mod x 10]
Mati Tealdi…
Lo que yo pense es mas o menos asi corrijanme si esta mal…
lista_bin :: [Int] -> Bool lista_bin [] = False lista_bin (x:xs) = Para_todo (cero_o_uno) (x:xs)
use dos funciones que ya habia definido antes…
Andrés…
(nwolovick) esta bien, solo que podes poner directamentelista_bin :: [Int] -> Bool lista_bin xs = paraTodo ceroOUno xs where ceroOUno :: Int -> Bool ceroOUno x = x==0 || x==1> no es necesario dividir en el caso base y el inductivo, esto ya lo hace
paraTodo
.
listabin :: [Int] -> Bool listabin [] = False listabin [x] |x==1 || x==0 = True |otherwise = False listabin (x:xs) |(x==1 || x==0) && listabin xs = True |otherwise = False
Ejercicio 18
Generalizo el ejercicio 17 para cualquier par de numeros (creo que a eso se referia):
listbin3 :: Int -> Int -> [Int] -> Bool listbin3 x y [] = False listbin3 x y [q] = [q]==[x]||[q]==[y] listbin3 x y (m:ms) = ([m]==[x]||[m]==[y])&&listbin3 x y ms
Yo queria hacerlo para que me devolviera una cadena de caracteres y lei que habia una función chr que transformaba un Int en un char de acuerdo al mapa de caracteres y eso me hubiera servido, pero en mi hugs no estaba por lo que tuve que hacer algo como esto:
digtochr :: Int -> String digtochr 0 = "0" digtochr 1 = "1" digtochr 2 = "2" digtochr 3 = "3" digtochr 4 = "4" digtochr 5 = "5" digtochr 6 = "6" digtochr 7 = "7" digtochr 8 = "8" digtochr 9 = "9" listtochr :: [Int] -> String listtochr [] = "" listtochr [x] = digtochr x listtochr (x:xs) = (digtochr x) ++ (listtochr xs) numtochr :: Int -> String numtochr x = listtochr (digi x) listbin4 :: Int -> Int -> [Int] -> String listbin4 x y ms |ms==[] = "Es vacia" |listbinS x y ms = "Solo tiene "++(numtochr x)++"'s y "++(numtochr y)++"'s" |otherwise ="Tiene mas que "++(numtochr x)++"'s y "++(numtochr y)++"'s" where listbinS :: Int -> Int -> [Int] -> Bool listbinS x y [] = False listbinS x y [m] = [m]==[x]||[m]==[y] listbinS x y (m:ms) = ([m]==[x]||[m]==[y])&&listbinS x y ms
Me parecio una obscenidad de funciones solo para lograr eso por lo que hice esta alternativa:
listbin5 :: Int -> Int -> [Int] -> String listbin5 x y ms |ms==[] = "Es vacia" |listbinS x y ms = "Solo tiene esos numeros" |otherwise ="Tiene mas que esos numeros" where listbinS :: Int -> Int -> [Int] -> Bool listbinS x y [] = False listbinS x y [m] = [m]==[x]||[m]==[y] listbinS x y (m:ms) = ([m]==[x]||[m]==[y])&&listbinS x y ms
Definí paraTodo asi:
paraTodo :: (a -> Bool) -> [a] -> Bool paraTodo pre [] = True paraTodo pre (x:xs) = pre x && paraTodo pre xs
y redefiní la funcion asi:
listbin6 :: [Int] -> Bool listbin6 [] = True listbin6 (xs) = paraTodo (listbinopt) (xs) where listbinopt :: Int -> Bool listbinopt x = x==1||x==0
para redefinir la funcion generalizada no se me ocurrio como hacerlo con paraTodo por lo que defini la funcion paraTodos:
paraTodos :: (a -> a -> a -> Bool) -> a -> a -> [a] -> Bool paraTodos pre x y [] = True paraTodos pre x y (m:ms) = pre x y m && paraTodos pre x y ms
y luego
listbin7 :: Int -> Int -> [Int] -> Bool listbin7 x y [] = True listbin7 x y xs = paraTodos listbinopt x y xs where listbinopt :: Int -> Int -> Int -> Bool listbinopt x y z = z==x||z==y
José Neder
(nwolovick) El ejercicio se resuelve solo conparaTodo
ylistbin6
lo demás es mucho mucho ruido. Hay que simplificar, complicar siempre se puede.
Ya lo vi, Soy un tonto, si lo podria haber escrito casi igual asi:
listbin8 :: Int -> Int -> [Int] -> Bool listbin8 x y [] = True listbin8 x y xs = paraTodo (listbinopt x y) xs where listbinopt :: Int -> Int -> Int -> Bool listbinopt x y z = z==x||z==y
Me parese q esta puede se otra solucion posible… No me fijo solo si los num de la lista son 0 o 1 sino si son binarios de mas de un digito…
listabin18 :: [Int] -> Bool listabin18 [] = False --Defino q si la lista esta vacia no hay num binarios XD listabin18 [x] = binall x listabin18 xs = paraTodo binall xs
paraTodo :: (a -> Bool) -> [a] -> Bool paraTodo p [] = False paraTodo p [x] = p x paraTodo p (x:xs) = p x && paraTodo p xs binall :: Int -> Bool binall n = bin (dig n) bin :: [Int] -> Bool bin [x] = (x==0 || x==1) bin (x:xs) = (x==0 || x==1) && bin xs dig :: Int -> [Int] dig x | x>=0 && x<10 = [x] | x>= 10 = dig (div x 10) ++ [mod x 10]
Mati Tealdi…
Ejercicio 19
Con no multiplo lo hice asi:
noMultiplo :: Int -> Int -> Bool noMultiplo p n = mod p n > 0
primo1 :: Int -> Bool primo1 1 = False primo1 2 = True primo1 x = paraTodo (noMultiplo x) (desdeHasta 2 (x-1))
o lo que es lo mismo:
primo2 :: Int -> Bool primo2 1 = False primo2 2 = True primo2 x = paraTodo (noMultiplo x) [2..(x-1)]
Tambien se me ocurrió hacerlo asi:
primo3 :: Int -> Bool primo3 1 = False primo3 2 = True primo3 x = listno0 (map (mod x) (desdeHasta 2 (x-1))) where listno0 :: [Int] -> Bool listno0 [] = True listno0 (x:xs) = x/=0 && listno0 xs
Pero estas funciones como las veo yo tienen un defecto que es que hay que definir los casos 1 y 2 por lo que hice esta otra, definiendo filtro:
multiplo :: Int -> Int -> Bool multiplo p n = mod p n == 0
filtro :: (a -> Bool) -> [a] -> [a] filtro p [] = [] filtro p (x:xs) | p x = [x: filtro p xs] | otherwise = [filtro p xs]
o comprensivamente
filtro2 :: (a -> Bool) -> [a] -> [a] filtro2 p xs = [ x | x <- xs, p x ]
primo4 :: Int -> Bool primo4 x = filtro (multiplo x) (desdeHasta 1 x)==[1,x]
Ahora tambien sabemos que si no tiene divisores hasta la raiz, es primo por lo que defini esta otra, un poco mas complicada, gracias a la funcion definida por “” y utilizando dos funciones de Haskell (sqrt y round):
primo5 :: Int -> Bool primo5 1 = False primo5 x = (filtro (multiplo x) (desdeHasta 1 (round (sqrt (fromIntegral x)))))==[1]
yo éste lo hice así:
definí en los ejercicios anteriores paraTodo y noMultiplo:
paraTodo :: (a -> Bool) -> [a] -> Bool paraTodo p [] = False paraTodo p [x] = p x == True paraTodo p (x:xs) = (paraTodo p xs) && (p x == True) noMultiplo :: Int -> Int -> Bool noMultiplo x y = x`mod`y >0
Y luego, primo:
primo :: Int -> Bool primo n = paraTodo (noMultiplo n) (desdeHasta 2 (n-1))
EMMANUEL GUNTHER
Yo lo compliqué un poco, pero solamente para que sea un poco más rápido. El tema es que había problema de tipos, asique tuve que crear 2 funciones, una que ya está en este wiki (int2float) y su inversa, int2float.
int2float :: Int -> Float int2float = fromIntegral
float2int :: Float -> Int float2int = round
primo :: Int -> Bool primo n | (abs n) > 2 = paraTodo (noMultiplo n) (desdeHasta 2 (float2int(sqrt(int2float(abs(n)))))) | (abs n) == 2 = True | otherwise = False
Lo que tiene esta funcion primo es que no busca desde 2 hasta n-1, sino hasta la raíz cuadrada de n porque se supone que si no es primo, entonces es divisible por algún numero menor a la raiz cuadrada del mismo. Si intentás poner en la función hasta n-1 el número que aparece al último del taller3, pues tarda muchísimo. Mientras que ésta función te devuelve el resultado en segundos. Obviamente que seguro se puede optimizar aún más.
(Pedro A.)
(nwolovick) Propongo la siguiente optimización probar que no es multiplo de 2,3,5,7 … sqrt(n-1), con lo que nos ahorramos todo los pares salvo el 2. Si ya no es multiplo de 2 pues no va ser de 4 ni de 6, etc. Ahi tenemos un 50% mas de velocidad.
(José Neder) Aca esta la optimizacion que faltaba utilizando solo los primos hasta el cuadrado.
primoshasta3 :: Int -> [Int] primoshasta3 x = cabeza (primhasta x)
primhasta :: Int -> [[Int]] primhasta x = plegarI primo7 [] [2..x] where primo7 :: [[Int]] -> Int -> [[Int]] primo7 [] n = [[n],[n]] primo7 [xs,(y:ys)] n | todocump (multiplo n) ys==[] && y*y>n = [xs++[n],(y:ys)] | y*y==n = [xs,tomar (length (y:ys)) xs:(y:ys)] | otherwise = [xs,(y:ys)] tomar :: Int -> [a] -> a tomar 0 (x:xs)=x tomar n (x:xs)=tomar (n-1) xs
Ejercicio 20
Con filtro es algo asi asi:
primoshasta :: Int -> [Int] primoshasta x = filtro primo1 (desdeHasta 1 x)
primoshast2 :: Int -> [Int] primoshast2 x = filtro primo1 (desdeHasta 1 x)
mas rapido con la definicion de primo con raiz
primoshast3 :: Int -> [Int] primoshast3 x = filtro primo5 (desdeHasta 1 x)
José Neder
Ejercicio 21
Ahora que lo se aca esta la solucion:
eqlist :: Eq a => [a] -> [a] -> Bool eqlist [] [] = True eqlist xs [] = False eqlist [] ys = False eqlist (x:xs) (y:ys) | x==y = eqlist xs ys | otherwise = False
Esto sirve para listas con los mismos tipos de elementos, pero todavia no se como hacer para que sirva para listas con distintos tipos de elementos, es decir, que me devuelva False cuando esto suceda…
(nwolovick) No creo que se pueda. Fijate que la definición no es muy prolija los casos 2 y 3 pueden ponerse de manera equivalente pero que sean mas legibles.(nwolovick) Fijense que hay redundancia por no usar calculo de predicados!
El casoeqlist (x:xs) (y:ys) | x==y = eqlist xs ys | otherwise = FalseEs equivalente a
eqlist (x:xs) (y:ys) = x==y && eqlist xs ysque resulta más corto y más entendible.
Recomiendo esta pagina: http://www.cs.kent.ac.uk/people/staff/sjt/craft2e/ es la pagina de libro “Haskell: The Craft of Functional Programming, Second Edition” y dice casi todo a cerca de Haskell.
(nwolovick) Es el libro que estoy usando! Muy recomendable salvo que esta en inglé.
José Neder
Ejercicio 22
En este ejercicio amí se me ocurrió una variante. “Dadas dos listas desordenadas, devolver la mezcla ordenada. El que lo quiera resolver, deje de leer en este punto. La solución a continuación:
mixorder :: [Int] -> [Int] -> [Int] mixorder [] [] = [] mixorder xs [] = order xs mixorder [] ys = order ys mixorder xs (y:ys) = mixorder (filter (<y) xs) (filter (<y) ys) ++ [y] ++ mixorder (filter (>=y) xs) (filter (>=y) ys)
Noten que llamo a otra función denominada order. Esta función está publicada en el ejercicio que sigue (ejercicio_23). (Pedro A.)
(José Neder) En el ejercicio dice “Defina una función tal que, dadas dos listas de naturales ordenadas xs y ys, retorne la “mezcla ordenada” de las listas, es decir la lista ordenada compuesta por los elementos de xs y ys. Ayuda: piense como procedería con 2 mazos de cartas.”
aca lo hice con plegarI
buscmin :: Ord a => [a] -> a -> [a] buscmin [] x = [x] buscmin (x:xs) y | y<=x = y:(x:xs) | otherwise = x:(buscmin xs y)
mixlists1 :: Ord a => [a] -> [a] -> [a] mixlists1 xs ys = plegarI (buscmin) [] (xs++ys)
si no se debe repetir
buscmin2 :: Ord a => [a] -> a -> [a] buscmin2 [] x = [x] buscmin2 (x:xs) y | y<x = y:(x:xs) | y==x = (x:xs) | otherwise = x:(buscmin2 xs y)
mixlists2 :: Ord a => [a] -> [a] -> [a] mixlists2 xs ys = plegarI (buscmin2) [] (xs++ys)
Ejercicio 23
la unica forma que se me ocurrio fue esta, espero que se me ocurra una mejor:
mayorig :: Int -> Int -> Bool mayorig x y = x>=y
sacMax :: [Int] -> [Int] sacMax [] = [] sacMax (x:xs) |paraTodo (mayorig x) xs = xs |otherwise = x:(sacMax xs)
tomarMax :: [Int] -> Int tomarMax [x] = x tomarMax (x:xs) |paraTodo (mayorig x) xs = x |otherwise = tomarMax xs
ordenar :: [Int] -> [Int] ordenar [] = [] ordenar xs = (ordenar (sacMax xs)) ++ [tomarMax xs]
o si saco el menor de la lista
(nwolovick) Esto se llama ordenar por inserción.
menorig :: Int -> Int -> Bool menorig x y = x<=y
sacMin :: [Int] -> [Int] sacMin [] = [] sacMin (x:xs) |paraTodo (menorig x) xs = xs |otherwise = x:(sacMin xs)
tomarMin :: [Int] -> Int tomarMin [x] = x tomarMin (x:xs) |paraTodo (menorig x) xs = x |otherwise = tomarMin xs
ordenar1 :: [Int] -> [Int] ordenar1 [] = [] ordenar1 xs = (tomarMin xs):(ordenar (sacMin xs))
José Neder
(nwolovick) totalmente equivalente a la anterior, no?
José, aquí presento otra solución, basada en lo que hablé con Nicolás acerca de quicksort. (Pedro A.)
order :: [Int] -> [Int] order [] = [] order (x:xs) = order (filter (<=x) xs) ++ [x] ++ order (filter (>x) xs)
- Acá hay otra solucióm para ordenar:
ordenar :: [Int] -> [Int] ordenar [x] = [x] ordenar xs = mezcla (ordenar derecha) (ordenar izquierda) where derecha = take mitad xs izquierda = drop mitad xs mitad = length xs `div` 2
Emmanuel Gunther (con ayuda de Nicolás W.)
(José Neder) Acabo de revizar esa funcion de arriba Emmanuel, no se bien a que te referis con mezcla porque esa funcion no existe. si es la de la “mezcla ordenada” esa la definimos con ordenar asi que no tiene sentido. Ademas, en todo caso, es una funcion mas general… Aca hice una con plegarI
buscmin :: Ord a => [a] -> a -> [a] buscmin [] x = [x] buscmin (x:xs) y | y<=x = y:(x:xs) | otherwise = x:(buscmin xs y)
ordenar2 :: Ord a => [a] -> [a] ordenar2 xs = plegarI (buscmin) [] (xs)
(nwolovick) Está todo bien con la solución de Emmanuel, simplementemezcla
es la mezcla ordenada de dos listas que ya están ordenadas.
Ejercicio 24
mapear :: (a -> b) -> [a] -> [b] mapear f [] = [] mapear f (x:xs) = (f x):(mapear f xs)
duplicar3 :: [Int] -> [Int] duplicar3 xs = mapear (* 2) (xs)
multiplicar3 :: [Int] -> Int -> [Int] multiplicar3 xs r = mapear (* r) (xs)
José Neder
Ejercicio 25
filtro :: (a -> Bool) -> [a] -> [a] filtro p [] = [] filtro p (x:xs) | p x = (x:filtro p xs) | otherwise = filtro p xs
filtro2 :: (a -> Bool) -> [a] -> [a] filtro2 p xs = [x|x <- xs, p x]
listbin8 :: Int -> Int -> [Int] -> Bool listbin8 x y [] = False listbin8 x y xs = cardinal (filtro (y==) xs++filtro (x==) xs)==cardinal xs
José Neder
Ejercicio 26
plegarI :: (a -> b -> a) -> a -> [b] -> a plegarI f z [] = z plegarI f z (x:xs) = plegarI f (f z x) xs
–Rep12
sumatoria3 :: [Float] -> Float sumatoria3 xs = (plegarI (+) (0) (xs))
sumatoria4 :: Num a => [a] -> a sumatoria4 xs = (plegarI (+) (0) (xs))
cardinal :: [a] -> Int cardinal xs = (plegarI (sum1) (0) (xs)) where sum1 :: Int -> a -> Int sum1 a b = a+1
–Rep17
listbin9 :: [Int] -> Bool listbin9 xs = (plegarI (equiv) (True) xs) where equiv :: Bool -> Int -> Bool equiv b n = b&&(1==n||0==n)
–Rep18
listbin10 :: Int -> Int -> [Int] -> Bool listbin10 x z xs = (plegarI (equiv x z) (True) xs) where equiv :: Int -> Int -> Bool -> Int -> Bool equiv x z b n = b&&(x==n||z==n)
–Res13
peginv :: [a] -> a -> [a] peginv [] n = [n] peginv (x:xs) n = n:(x:xs)
reversa3 :: [a] -> [a] reversa3 xs = (plegarI (peginv) [] xs)
–tambien se puede usar la funcion flip
flip1 :: (a -> b -> c) -> b -> a -> c flip1 f x y = f y x
reversa4 :: [a] -> [a] reversa4 xs = (plegarI (flip1 (:)) [] xs)
–Res21
eqlist2 :: Eq a => [a] -> [a] -> Bool eqlist2 xs ys | length xs == length ys = plegarI (eq) True (zip xs ys) | otherwise = False where eq :: Eq a => Bool -> (a,a) -> Bool eq a (b,c) = a&&b==c
–Res24
mapear2 :: (a -> b) -> [a] -> [b] mapear2 f xs = (plegarI (concaf f) [] xs) where concaf :: (a -> b) -> [b] -> a -> [b] concaf f ys e = (ys++[f e])
–Res25
filtro3 :: (a → Bool) → [a] → [a]
filtro3 p xs = plegarI (fil1 p) [] xs where fil1 :: (a -> Bool) -> [a] -> a -> [a] fil1 p xs x | p x = xs++[x] | otherwise = xs
José Neder
Ejercicio 27
perm :: [Int] → [Int] → Bool
perm [] [] = True
perm xs ys = lsiguales (ordenar xs) (ordenar ys)
Juanchi…
(José Neder) No hace falta aclarar que es True para dos listas vacia ya que “ordenar [] = []” y eqlist2 [] [] = True
uperm :: Ord a => [a] -> [a] -> Bool uperm xs ys = (eqlist2 (ordenar xs) (ordenar ys))
Ejercicio 28
(José Neder)Ya lo hice no era tan dificil, aunque no se si hay una forma mejor
Defini esto primero:
inter :: a -> [a] -> Int -> [a] inter a xs 0 = a:xs inter a (x:xs) (n+1)= x:(inter a xs n)
interc :: a -> [a] -> [[a]] interc a xs = map (inter a xs) [0..(length xs)]
concatodo :: [[a]] -> [a] concatodo [] = [] concatodo (x:xs) = x++concatodo xs
interca :: [[a]] -> a -> [[a]] interca xs y = concatodo (map (interc y) xs)
Y luego
permdexs :: [a] -> [[a]] permdexs [] = [[]] permdexs (x:xs) = interca (permdexs xs) x
o con plegarI
permdexs2 :: [a] -> [[a]] permdexs2 xs = plegarI interca [[]] xs
Ejercicio 29
expo :: Int → Int
expo 0 = 1
expo x = (x*0+2 ) * expo (x-1)
Juanchi…
(José Neder)??? porque asi? si es 2 a la n. Yo lo hice asi:
dosalan :: Int -> Int dosalan 0 = 1 dosalan n = 2*(dosalan (n-1))
con plegarI:
dosalan2 :: Int -> Int dosalan2 n = plegarI (pordos) 1 [1..n] where pordos :: Int -> Int -> Int pordos z n = z*2
utilizando las funciones de Haskell, ya esta definido mas general asi que se puede hacer asi, para cualquier n:
dosalan3 :: Float → Float dosalan3 n = 2 aca van 2 estellitas n
Ejercicio 30
Este lo hizo Pedro durante el practico 23/05
Primero definimos una funcion sumar
sumar:: [Int] -> [Int] -> [Int] sumar [] ys = ys sumar xs [] = xs sumar (x:xs) (y:ys) = (x+y) : sumar xs ys
que nos servira para hacer la relacion de suma entre las distintas filas de del triangulo de pascal (notar que si agregagamos un 0 a una fila y le aplicamos sumar con la misma fila nos da la siguiente fila….)
ej:
1 = [1] 1 1 = sumar (0:[1]) [1] = [1,1] 1 2 1 = sumar (0:[1,1]) [1,1] = [1,2,1] 1 3 3 1 = sumar (0:[1,2,1]) [1,2,1] = [1,3,3,1]
una vez que nos damos cuenta de esto definimos la funcion pascal:
pascal:: Int -> [[Int]] pascal 0 = [[]] pascal 1= [[1]] pascal (x+1) = sumar (0: (cabeza(pascal x))) (cabeza (pascal x)) : pascal x
aca obtenemos las filas desde n hasta 1 para ordenarlas deberiamos aplicarle un reverse a la funcion (si se desea)
Grande Pedro!!!!!!!!!!!!!!!!!!!!!!!!!!! Esta muy bueno gracias…… Andres Pozo…
aclaro que seria mas correcto poner lo siguiente
pascal:: Int -> [[Int]] pascal 0 = [[]] pascal 1= [[1]] pascal (x+2) = sumar (0: (cabeza(pascal (x+1)))) (cabeza (pascal (x+1))) : pascal (x+1)
ya que si x=0 cae en dos casos distintos…
(José Neder) Acabo de probar su version y es increiblemente ineficaz la mia es un poco mas larga de escribir, pero sale al toque y va de en orden no hace falta hacer un reverse. El problema con la suya es que va al reves por lo que la maquina debe acumular demasiada informacion. Yo lo hice de otras dos formas, esta es la mas ineficaz pensando en el coeficiente binominal:
fact :: Int -> Int fact 0 = 1 fact (n+1) = (n+1)*(fact n)
coefbin :: Int -> Int -> Int coefbin n m = div (div (fact n) (fact (n-m))) (fact m)
listnpas :: Int -> [Int] listnpas n = map (coefbin n) [0..n]
tripascal :: Int -> [[Int]] tripascal n = map listnpas [0..n]
(José Neder) Admito que la anterior es peor, solo da hasta el 14 en Hugs pero con las las propiedades del triangulo de Pascal defini esta:
p' :: [Int] -> [Int] p' [x] = [x] p' (x:xs) = (x+cabeza xs):(p' xs)
p :: [Int] -> [Int] p (x:xs) = x:(p' (x:xs))
listnpas2 :: Int -> [Int] listnpas2 0 = [1] listnpas2 (n+1) = p (listnpas2 n)
ahora con map:
tripascal2 :: Int -> [[Int]] tripascal2 n = map listnpas2 [0..n]
y con plegarI: gs
tripascal3 :: Int -> [[Int]] tripascal3 n = plegarI (listpasn3) [] [0..n] where listpasn3 :: [[Int]] -> Int -> [[Int]] listpasn3 xs n = xs++[listnpas2 n]
(nwolovick) Hay una forma de tener lo mejor de ambos mundos, por un lado la claridad de la definición de Andrés y la velocidad de José.
Hice un experimento activando el “medidor de pasos de reducción” que trae Hugs
Main> :set +s Main> pascal 10 [[1,9,36,84,126,126,84,36,9,1],[1,8,28,56,70,56,28,8,1],[1,7,21,35,35,21,7,1],[1,6,15,20,15,6,1],[1,5,10,10,5,1],[1,4,6,4,1],[1,3,3,1],[1,2,1],[1,1],[1]] (68468 reductions, 103162 cells) Main> tripascal3 9 [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1],[1,5,10,10,5,1],[1,6,15,20,15,6,1],[1,7,21,35,35,21,7,1],[1,8,28,56,70,56,28,8,1],[1,9,36,84,126,126,84,36,9,1]] (2790 reductions, 4082 cells)
Tenemos 68000 reducciones de la versión fácil de leer, contra 2790 de la versión rápida pero inentendible.
Parecería que la claridad tiene un precio alto.
Si embargo hacemos, podemos modificar la versión de Andrés para que use definiciones locales y calcule solo 1 vez cada una de las filas.
pascal':: Int -> [[Int]] pascal' 0 = [[]] pascal' 1= [[1]] pascal' (x+2) = sumar (0: filaAnterior) filaAnterior : trianguloAnterior where filaAnterior = cabeza trianguloAnterior trianguloAnterior = pascal' (x+1)
Aca tenemos un versión aun más legible que la de Andrés y veamos cuantas reducciones necesita para hacer 9 filas del Triángulo de Tartaglia.
Main> pascal' 10 [[1,9,36,84,126,126,84,36,9,1],[1,8,28,56,70,56,28,8,1],[1,7,21,35,35,21,7,1],[1,6,15,20,15,6,1],[1,5,10,10,5,1],[1,4,6,4,1],[1,3,3,1],[1,2,1],[1,1],[1]] (1065 reductions, 1695 cells)
De nuevo, tenemos lo mejor de dos mundos, claridad y velocidad, el ingrediente clave fue la utilización de definiciones locales que hacen que el cómputo del triángulo anterior y de la fila anterior solo se hagan 1 vez y se comparta en la fila superior.
(José Neder)Utilizando la idea de definirlo con definiciones locales redefino la funcion asi:
p3' :: [Int] -> [Int] p3' [x] = [x] p3' (x:y:ys) = x+y:p3' (y:ys)
p3 :: [Int] -> [Int] p3 (x:xs) = x:p3'(x:xs)
tripascal7 :: Int -> [[Int]] tripascal7 0 = [[1]] tripascal7 n = p3 x:(x:xs) where (x:xs) = tripascal7 (n-1)
Practico8> tripascal7 9 [[1,9,36,84,126,126,84,36,9,1],[1,8,28,56,70,56,28,8,1],[1,7,21,35,35,21,7,1],[1,6,15,20,15,6,1],[1,5,10,10,5,1],[1,4,6,4,1],[1,3,3,1],[1,2,1],[1,1],[1]] (807 reductions, 1444 cells)
(José Neder) aca la volvi a hacer de la manera que yo lo habia hecho y es un poquito mas rapida
foldnr :: (a -> a) -> a -> Int -> a foldnr a x 0 = a x foldnr a x n = a (foldnr a x (n-1))
p2' :: [Int] -> [Int] p2' [x] = [x] p2' (x:y:ys) = x+y:p2' (y:ys)
p2 :: [[Int]] -> [[Int]] p2 [] = [[1]] p2 ((x:xs):ys) = (x:p2' (x:xs)):(x:xs):ys
tripascal6 :: Int -> [[Int]] tripascal6 n = foldnr p2 [] n
Practico8> tripascal6 9 [[1,9,36,84,126,126,84,36,9,1],[1,8,28,56,70,56,28,8,1],[1,7,21,35,35,21,7,1],[1,6,15,20,15,6,1],[1,5,10,10,5,1],[1,4,6,4,1],[1,3,3,1],[1,2,1],[1,1],[1]] (788 reductions, 1434 cells)
Clase 4
(José Neder) a frecuencia lo defini directamente con plegarI.
esta :: Eq a => a -> [a] -> Bool esta a [] = False esta a (x:xs) = x==a||esta a xs
frecuencia :: [Palabra] -> [(Palabra,Int)] frecuencia xs = foldl contarpalabras [] xs where contarpalabras :: [(Palabra,Int)] -> Palabra -> [(Palabra,Int)] contarpalabras [] x = [(x,1)] contarpalabras ((a,b):xs) x | x==a = (a,b+1):xs | otherwise = (a,b):contarpalabras xs x
(José Neder) Para que identifique todos los caracteres que no son letras y no distinga entre mayusculas y minisculas, lo defino a partir de las letras:
letrasM :: String letrasM = ['A'..'Z']++['À'..'Ö']++['Ù'..'Ý']
letrasm :: String letrasm = ['a'..'z']++['à'..'ö']++['ù'..'ý']
numdea :: Eq a => a -> [a] -> Int numdea a (x:xs) | a==x = 0 | otherwise = 1+numdea a xs
tomarun :: Int -> [a] -> a tomarun 0 (x:xs)=x tomarun n (x:xs)=tomarun (n-1) xs
palabramin :: String -> String palabramin []=[] palabramin (x:xs) |esta x letrasM = tomarun (numdea x letrasM) letrasm:palabramin xs |otherwise = x:palabramin xs
tiraTodo :: String -> String tiraTodo [] = [] tiraTodo (x:xs) | esta x letrasm ||esta x letrasM = x:xs | otherwise = tiraTodo xs
tomaPalabra2 :: String -> String tomaPalabra2 [] = [] tomaPalabra2 (x:xs) | esta x letrasm ||esta x letrasM = x : tomaPalabra2 xs | otherwise = []
tiraPalabra2 :: String -> String tiraPalabra2 [] = [] tiraPalabra2 (x:xs) | esta x letrasm ||esta x letrasM = tiraPalabra2 xs | otherwise = x:xs
partirPalabras2 :: String -> [Palabra] partirPalabras2 st = partirPalabras2' (tiraTodo st)
partirPalabras2' :: String -> [Palabra] partirPalabras2' [] = [] partirPalabras2' st = (tomaPalabra2 st) : partirPalabras2' (tiraTodo (tiraPalabra2 st))
frecuencia' :: [Palabra] -> [(Palabra,Int)] frecuencia' xs = foldl contarpalabras [] xs where contarpalabras :: [(Palabra,Int)] -> Palabra -> [(Palabra,Int)] contarpalabras [] x = [(palabramin x,1)] contarpalabras ((a,b):xs) x | a==palabramin x = (a,b+1):xs | otherwise = (a,b):contarpalabras xs x
ordenarpar :: Ord b => [(a,b)] -> [(a,b)] ordenarpar xs = foldl (buscparmin) [] (xs) where buscparmin :: Ord b => [(a,b)] -> (a,b) -> [(a,b)] buscparmin [] x = [x] buscparmin ((a,b):xs) (c,d) | d>=b = (c,d):((a,b):xs) | otherwise = (a,b):buscparmin xs (c,d)
he aqui una implementacion de lo aprendido pero sobre otro lenguaje en este caso c:
FUNCIÓN FACTORIAL # include <stdio.h> long factorial(long);
main() {
int i;
printf(“Digite un entero: “); scanf(“%ld”, &n); for(i = 1;i ⇐ n; i ++) printf(“%2d! = %ld\n”,i ,factorial(i));
return 0;
}
long factorial(long number) {
if (number <= 1) return 1; else return (number * factorial (number-1));
}
y disculpen por meterme hablando de otro tema, pero aquellos que creen saber programar en c seguramente ahora lo veran de otra forma