Haskell/Soluções: diferenças entre revisões

[edição não verificada][edição não verificada]
Conteúdo apagado Conteúdo adicionado
m <source> -> <syntaxhighlight> (phab:T237267)
 
Linha 7:
# Defina uma função que subtraia 12 da metade de seu argumento.}}
 
#<sourcesyntaxhighlight lang=haskell>
quadruplicar x = dobrar (dobrar x)
quadruplicar 5 = dobrar (dobrar 5)
quadruplicar 5 = dobrar 10
quadruplicar 5 = 20
</syntaxhighlight>
</source>
#<sourcesyntaxhighlight lang=haskell>
subtrairMetade x = x / 2 - 12
</syntaxhighlight>
</source>
 
{{Exercícios|1=
Linha 21:
# Aproximadamente, de quantas pedras as famosas pirâmides de Gizé feitas? Dica: você vai precisar estimar o volume da pirâmide e o volume de cada bloco de pedra.}}
 
#<sourcesyntaxhighlight lang=haskell>volumeCaixa b a l = b * a * l</sourcesyntaxhighlight><br/>
#Uma possível solução para o problema da pirâmide seria: calcular o volume da pirâmide como se ela fosse ideal e dividir tal valor pelo volume estimado de um bloco de pedra. Por exemplo, no GHCi:
 
Linha 33:
# Escreva uma função que calcule o volume de um cilindro. O volume de um cilindro é a área da base, que é um círculo, multiplicada pela altura. Como você já programou a função da área de um círculo neste capítulo, você pode reutilizá-la aqui.}}
 
#<sourcesyntaxhighlight lang=haskell>
area r = pi * r ^ 2
volumeCil r h = h * area r
</syntaxhighlight>
</source>
 
== [[Haskell/Tipos básicos|Tipos básicos]] ==
Linha 59:
}}
 
#<sourcesyntaxhighlight lang="haskell">
negativo :: Int -> Int
</syntaxhighlight>
</source>
#<sourcesyntaxhighlight lang="haskell">
(||) :: Bool -> Bool
</syntaxhighlight>
</source>
#<sourcesyntaxhighlight lang="haskell">
diasNoMes :: Bool -> Int -> Int
</syntaxhighlight>
</source>
#<sourcesyntaxhighlight lang=haskell>
f :: Int -> Int -> Bool
</syntaxhighlight>
</source>
#<sourcesyntaxhighlight lang=haskell>
g :: Int -> Int
</syntaxhighlight>
</source>
 
== [[Haskell/Listas e n-uplas|Listas e n-uplas]] ==
Linha 92:
 
# Não. Todos os elementos de uma lista devem ser do mesmo tipo. Em <code>3:[rue,False]</code> tenta-se adicionar um número a uma lista de Bools, o que não é permitido.
# <sourcesyntaxhighlight lang=haskell>
cons8 lista = 8:lista
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang=haskell>
cons8' lista = lista ++ [8]
</syntaxhighlight>
</source>
# <code> let meuCons lista elemento = elemento : lista</code>
 
Linha 181:
# Uma possível solução seria: <code>snd (fst (("Ola", 4), True))</code>.
# Sim, pois uma dupla permite que seus elementos sejam de qualquer tipo, ao contrário de uma lista que exige que todos os elementos sejam do mesmo tipo.
# <sourcesyntaxhighlight lang=haskell>
cabecaCauda lista = (head lista, tail lista)
</syntaxhighlight>
</source>
#<sourcesyntaxhighlight lang=haskell>
quintoElemento lista = head (tail (tail (tail (tail lista))))
</syntaxhighlight>
</source>
::Para criar a função <code>quintoElemento</code> precisamos repetir <code>tail</code> quatro vezes. Toda essa repetição deixa o código mais propenso a erros e mais difícil de ler.
 
Linha 199:
}}
 
# <sourcesyntaxhighlight lang=haskell>
cabecaCauda :: [a] -> (a, [a])
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang=haskell>
quintoElemento :: [a] -> a
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang=haskell>
h :: Int -> a -> b -> Char -- y e z não são usados, portanto, seus tipos não importam e nem precisam ser iguais
</syntaxhighlight>
</source>
 
== [[Haskell/Casamento de padrões, if e let|Casamento de padrões, if e let]] ==
Linha 218:
Reescreva a função <code>pts</code> usando guardas.}}
 
<sourcesyntaxhighlight lang="haskell">
pts :: Int -> Int
pts x
Linha 228:
| x == 6 = 1
| otherwise = 0
</syntaxhighlight>
</source>
 
{{Exercício|1=
Linha 239:
 
Uma possível solução seria escrever:
<sourcesyntaxhighlight lang="haskell">
pts :: Int -> Int
pts 1 = 10
Linha 246:
| x < 1 || x > 7 = 0
| otherwise = 7 - x
</syntaxhighlight>
</source>
 
{{Exercício|1=
Linha 252:
}}
 
<sourcesyntaxhighlight lang="haskell">
quarto :: (a,b,c,d,e,f,g,h,i,j) -> d
quarto (_,_,_,x,_,_,_,_,_,_) = x
</syntaxhighlight>
</source>
 
== [[Haskell/Entradas e saídas simples|Entradas e saídas simples]] ==
Linha 263:
 
Como abrir uma janela implica numa ação com consequências no ambiente externo, a saída de <code>abrirJanela</code> deve ser <code>IO</code>:
<sourcesyntaxhighlight lang=haskell>
abrirJanela :: JanelaTitulo -> JanelaTamanho -> IO Janela
</syntaxhighlight>
</source>
 
{{Exercício|1=
Linha 285:
Você deve vai precisar usar as funções <code>read</code> e <code>show</code> para converter o texto do usário em número, e depois o resultado numérico em texto.}}
 
<sourcesyntaxhighlight lang="haskell">
main :: IO ()
main =
Linha 294:
putStrLn ("A área do triângulo é " ++ show (area base altura) ++ ".")
where area b a = (read b :: Float) * (read a :: Float) / 2
</syntaxhighlight>
</source>
 
{{Exercício|1=
Linha 302:
}}
 
<sourcesyntaxhighlight lang="haskell">
main :: IO ()
main =
Linha 312:
then putStrLn "Acredito que Haskell pode ser aprendido por qualquer pessoa!"
else putStrLn "Desculpe-me, não te conheço."
</syntaxhighlight>
</source>
 
Uma possível solução usando casamento de padrões:
 
<sourcesyntaxhighlight lang="haskell">
main :: IO ()
main =
Linha 327:
resposta "Maria" = "Acredito que Haskell pode ser aprendido por qualquer pessoa!"
resposta _ = "Desculpe-me, não te conheço."
</syntaxhighlight>
</source>
 
= Introdutório =
Linha 337:
{{Exercício|1=
# O que acontece se calcularmos <code>fatorial (-1)</code>?
# Como já vimos antes, a ordem dos padrões é importante quando definimos uma função. Se fizéssemos <sourcesyntaxhighlight lang="haskell">
fatorial n = n * fatorial (n - 1)
fatorial 0 = 1
</sourcesyntaxhighlight> e tentássemos calcular <code>fatorial 6</code>, o que aconteceria?
# O ''fatorial duplo'' de um número é a multiplicação dos sucessores intercalados, partindo de 1 (ou 2) até o argumento. Por exemplo: o fatorial duplo de 8 é 8 × 6 × 4 × 2 = 384; o fatorial duplo de 7 é 7 × 5 × 3 × 1 = 105. Defina uma função <code>fatorialDuplo</code> em Haskell que faça esta conta.
}}
Linha 346:
# O compilador começaria a avaliar<br /><code>(-1) * fatorial (-2)</code><br /><code>(-1) * (-2) * fatorial (-3)</code><br /><code>(-1) * (-2) * (-3) * fatorial (-4)</code><br />e assim por diante. Como a definição de <code>fatorial</code> não possui nenhum caso que impeça essa expansão, o compilador começaria o processo mas nunca consegueria terminá-lo corretamente.
# O compilador avaliaria corretamente até <code>6 * 5 * 4 * 3 * 2 * 1 * fatorial 0</code>. Neste ponto, ele deveria encerrar a recursividade, mas como um argumento <code>0</code> satisfaz a condição do primeiro caso, o caso base nunca seria avaliado e a expansão do fatorial continuaria pelos números negativos. O resultado seria o mesmo que o do exercício anterior.
# <sourcesyntaxhighlight lang="haskell">
fatorialDuplo 1 = 1
fatorialDuplo 2 = 2
fatorialDuplo n = n * fatorialDuplo (n - 2)
</syntaxhighlight>
</source>
 
{{Exercício|1=
Linha 358:
}}
 
# <sourcesyntaxhighlight lang=haskell>
potencia _ 0 = 1
potencia x y = x * potencia x (y - 1)
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang=haskell>
adicao x 0 = x
adicao x y = adicao (maisUm x) (y - 1)
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang=haskell>
log2 1 = 0
log2 x = 1 + log2 (div x 2)
</syntaxhighlight>
</source>
 
=== Recrusividade usando listas ===
Linha 380:
# Redefina <code>length</code> usando uma função interna auxiliar e um parâmetro acumulador.}}
 
# <sourcesyntaxhighlight lang=haskell>
replicar :: Int -> a -> [a]
replicar 0 _ = []
replicar n x = x : replicar (n - 1) x
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang=haskell>
(!!!) :: [a] -> Int -> a
(x:xs) !!! 0 = x
(x:xs) !!! n = xs !!! (n - 1)
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang=haskell>
ziper :: [a] -> [b] -> [(a,b)]
ziper [] _ = []
ziper _ [] = []
ziper (x:xs) (y:ys) = (x,y) : ziper xs ys
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang=haskell>
minhaLength :: [t] -> Int
minhaLength l = go l 0
where go [] n = n
go (x:xs) n = go xs (n+1)
</syntaxhighlight>
</source>
 
== [[Haskell/Listas II|Listas II]] ==
Linha 409:
Defina a função recursiva <code>repetir :: a -> [a]</code> que cria listas infinitas. O Prelude possui a <code>repeat</code> que realiza a mesma tarefa.}}
 
<sourcesyntaxhighlight lang="haskell">
repetir :: a -> [a]
repetir x = x : repetir x
</syntaxhighlight>
</source>
 
 
Linha 431:
}}
 
# <sourcesyntaxhighlight lang="haskell">
tomarInt :: Int -> [a] -> [a]
tomarInt _ [] = []
tomarInt 0 _ = []
tomarInt n (x:xs) = x : tomarInt (n - 1) xs
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang="haskell">
eliminarInt :: Int -> [a] -> [a]
eliminarInt _ [] = []
eliminarInt 0 xs = xs
eliminarInt n (x:xs) = eliminar (n - 1) xs
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang="haskell">
somarInt :: [Int] -> Int
somarInt [] = 0
somarInt (x:xs) = x + somarInt xs
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang="haskell">
scanSoma :: [Int] -> [Int]
scanSoma [] = []
Linha 460:
aux tot (x:xs) = tot' : aux tot' xs
where tot' = x + tot
</syntaxhighlight>
</source>
# <sourcesyntaxhighlight lang="haskell">difs :: [Int] -> [Int]
difs [] = []
difs (x:[]) = []
Linha 471:
where aux _ [] = []
aux (a:as) (b:bs) = (b - a) : aux as bs
</syntaxhighlight>
</source>
 
== [[Haskell/Listas III|Listas III]] ==
Linha 487:
As funções do Prelude <code>and</code>, <code>or</code>, <code>maximum</code> e <code>minimum</code> correspondem às quatro primeiras funções destes exercícios, respectivamente.}}
 
# <sourcesyntaxhighlight lang="haskell">
-- Versão recursiva
eListas :: [Bool] -> Bool
Linha 505:
ouListas' :: [Bool] -> Bool
ouListas' ls = foldr (||) False ls
</syntaxhighlight>
</source>
#
# <sourcesyntaxhighlight lang="haskell">
maximo :: Ord a => [a] -> a
maximo = foldl1 max
Linha 513:
minimo :: Ord a => [a] -> a
minimo = foldl1 min
</syntaxhighlight>
</source>
#
# <sourcesyntaxhighlight lang="haskell">
inverter :: [a] -> [a]
inverter ls = foldl consInvertido [] ls
where consInvertido xs x = x : xs
</syntaxhighlight>
</source>
 
=== ''Scans'' ===
Linha 528:
}}
 
# <sourcesyntaxhighlight lang="haskell">
-- Versão recursiva
scanR :: (a -> b -> b) -> b -> [a] -> [b]
Linha 550:
scanL' f acum [] = [acum]
scanL' f acum ls = (scanL' f acum (init ls)) ++ [foldl f acum ls]
</sourcesyntaxhighlight> Vale notar que a <code>scanL'</code> (usando <code>foldl</code>) é menos eficiente que <code>scanL</code> (versão recursiva).
#
# <sourcesyntaxhighlight lang="haskell">
fatLista :: Int -> [Int]
fatLista n = scanl (*) 1 [2..n]
</syntaxhighlight>
</source>
 
=== Compreensão de listas ===
Linha 571:
}}
 
# <sourcesyntaxhighlight lang="haskell">
retornaDivisiveis :: Int -> [Int] -> [Int]
retornaDivisiveis n xs = [x | x <- xs, mod x n == 0]
</syntaxhighlight>
</source>
#
# <sourcesyntaxhighlight lang="haskell">
selecionaCaudas :: [[Int]] -> [[Int]]
selecionaCaudas ls = [xs | (x:xs) <- ls, x > 5]
</syntaxhighlight>
</source>
#
# Sim, a ordem importa. Todas as condições devem ser satisfeitas na ordem em que aparecem na compreensão: se tivermos cinco condições e segunda for falsa, a terceira, a quarta e a quinta não serão avaliadas.
#
# <sourcesyntaxhighlight lang="haskell">
mapear :: (a -> b) -> [a] -> [b]
mapear f xs = [f x | x <- xs]
Linha 589:
filtrar :: (a -> Bool) -> [a] -> [a]
filtrar f xs = [x | x <- xs, f x]
</syntaxhighlight>
</source>
#
# <sourcesyntaxhighlight lang="haskell">
fstComSndPar :: [(Int, Int)] -> [Int]
fstComSndPar ls = (map fst . filter faux) ls
where faux (x,y) = ePar y
</syntaxhighlight>
</source>
 
== [[Haskell/Declaração de tipos|Declaração de tipos]] ==
Linha 607:
}}
 
<sourcesyntaxhighlight lang="haskell">
data Data = Data Int Int Int -- ano, mês, dia
data Aniversario = Nascimento String Data -- nome, data
Linha 626:
romaoCasamento :: Aniversario
romaoCasamento = Casamento "João Romão" "Maria Romão" (Data 1987 3 4)
</syntaxhighlight>
</source>
 
 
Linha 632:
Reescreva a declarção de <tt>Aniversario</tt> usando o sinônimo <tt>Nome</tt>, e mantendo o uso de <tt>Data</tt>.}}
 
<sourcesyntaxhighlight lang="haskell">
type Nome = String
data Data = Data Int Int Int -- ano, mês, dia
data Aniversario = Nascimento Nome Data -- nome, data
| Casamento Nome Nome Data -- nome 1, nome 2, Data
</syntaxhighlight>
</source>
 
 
Linha 644:
# Trantado-se de registros e dentro de um mesmo módulo, por que dois campos, de dois tipos diferentes, não podem ter o mesmo nome?}}
 
# <sourcesyntaxhighlight lang="haskell">
data Data = Data {ano :: Int, mes :: Int, dia :: Int}
 
mostrarData :: Data -> String
mostrarData d = show (ano d) ++ "-" ++ show (mes d) ++ "-" ++ show (dia d)
</syntaxhighlight>
</source>
#
# Cada campo torna-se uma função de acesso. Sendo estas funções realacionadas as tipos distintos, suas assinaturas de tipos serão diferentes para cada caso. Entretanto, Haskell não permite que uma função tenha mais que uma assinatura de tipo. Em outras palavras, é impossível ter <code>campo1 :: Dado1 -> Int</code> e <code>campo1 :: Dado2 -> Int</code> no mesmo lugar, por exemplo.
Linha 659:
* <code>let f x y = read x + y in foldr f 1 xs</code>}}
 
* <sourcesyntaxhighlight lang="haskell">
map (\x -> x * 2 + 3) xs
</syntaxhighlight>
</source>
* <sourcesyntaxhighlight lang="haskell">
foldr (\x y -> read x + y) 1 xs
</syntaxhighlight>
</source>
 
{{Exercício|# Seções são açúcar sintático para expressões lambdas. Reescreva as seguintes seções na forma de lambdas determine seus tipos:
Linha 672:
#
# Teste as seguintes linhas no GHCi:
:: <sourcesyntaxhighlight lang="haskell">
norma3D x y z = sqrt (x^2 + y^2 + z^2)
norma3D' a b = a `norma3D` b
</syntaxhighlight>
</source>
:: Se a notação infixa só pode ser usada com funções de dois argumentos, por que a definição de <code>norma3D'</code> é válida? Os resultados de <code>norma3D</code> e <code>norma3D'</code> serão sempre iguais? Dica: observe os tipos de cada uma das funções e lembre-se de ''[[Haskell/Listas II#Currying|currying]]''.}}
 
#
## <sourcesyntaxhighlight lang="haskell">
(\x -> 4 + x) :: (Num a) => a -> a
</syntaxhighlight>
</source>
## <sourcesyntaxhighlight lang="haskell">
(\xs -> elem 1 xs) :: [a] -> Bool
</syntaxhighlight>
</source>
## <sourcesyntaxhighlight lang="haskell">
(\c -> notElem c "abc") :: Char -> Bool
</syntaxhighlight>
</source>
#
# A função <code>norma3D</code> possui três argumentos, portanto seu tipo é da forma <code>a -> a -> a -> a</code>. Como temos o efeito de ''currying'', podemos considerar que <code>norma3D</code> tem dois argumentos se pensarmos seu tipo como sendo <code>a -> a -> (a -> a)</code>, isto é, uma função de dois argumentos que retorna uma função de um argumento. Assim sendo, se usarmos a notação de ponto livre, isto é, omitindo o terceiro argumento, forçamos o ''currying'' e podemos usar a notação infixa. Portanto, <code>nomra3D' a b</code> é, repetindo, ''uma função de dois argumentos que retorna uma função de um argumento'', sendo que podemos usá-la com três argumentos, pois <code>(f m) n == f m n</code>. Também é fácil ver que os resultados serão sempre iguais se expandirmos a aplicação de <code>norma3D'</code>:
::<sourcesyntaxhighlight lang="haskell">
(norma3D' a b) c
(a `norma3D` b) c
(norma3D a b) c
norma3D a b c
</syntaxhighlight>
</source>
 
== [[Haskell/Casamento de padrões|Casamento de padrões]] ==
Linha 704:
 
Qualquer valor aplicado a <code>h</code> sempre retorna <tt>True</tt>. A definição de <code>h</code> é:
<sourcesyntaxhighlight lang="haskell">
h :: Int -> Bool
h k = True
h _ = False
</syntaxhighlight>
</source>
 
No primeiro caso, o padrão definido como <code>k</code> casa com qualquer valor de entrada. Portanto, o primeiro caso será sempre verdadeiro, o que faz com que <code>h</code> sempre retorne <tt>True</tt>. Assim sendo, não há nenhum valor que faça <code>h</code> retornar <tt>False</tt>.