Problema da árvore
Este é um problema bastante simples de se resolver em linguagens imperativas, a utilização de alguns ciclos for
resolve bem esta questão. Contudo, numa linguagem funcional como Haskell, em que os ciclos não existem, o pensamento a seguir deverá ser outro.
Há várias soluções possíveis, usando recursividade ou, como será apresentado de seguida, com recurso a listas por compreensão.
O código
Compactado
Esta solução não será a melhor, pois repete bastante a estrutura take $ repeat
e é mais obscura (não é tão fácil interpretar a lista por compreensão).
import qualified Data.Char as Char
mainarv :: IO()
mainarv = do
putStr "Quantos ramos (0-40) (<=0 para terminar)? "
n <- readLn :: IO Int
if n > 0 && n <= 40 then do
mapM_ putStrLn $ arvore n
mainarv
else if n <= 0 then
putStrLn "FIM."
else
putStrLn "Erro: ramos > 40!"
arvore :: Int -> [String]
arvore 0 = []
arvore ramos | ramos > 0 && ramos <= 40 = [(take (ramos-i) $ repeat ' ') ++ (take (i*2-1) $ repeat '*') | i <- [1..ramos]] ++ [(take (ramos-1) $ repeat ' ') ++ "*" | _ <- [1..3]]
| otherwise = []
Extendido
Vamos portanto substituir a função arvore
pela seguinte:
arvore :: Int -> [String]
arvore 0 = []
arvore ramos | ramos > 0 && ramos <= 40 = copa ramos ++ tronco ramos 3
| otherwise = []
where
tronco r l = [repeatChar ' ' (ramos-1) ++ "*" | _ <- [1..l]]
copa r = [repeatChar ' ' (ramos-i) ++ repeatChar '*' (i*2-1) | i <- [1..ramos]]
repeatChar ch n = take n $ repeat ch
Breve explicação
A função arvore
vai devolver uma lista de String
s, cada uma das quais representa uma linha da árvore. É por isto que, na função main
, fazemos um mapM_ putStrLn
(desta forma cada string da lista será colocada numa linha).
Esta função arvore
recebe tão-somente o número de ramos que deverá desenhar, e com esse número calcula tudo o que é necessário para a árvore.
Consideremos uma árvore com 5 ramos:
*Main> main
Quantos ramos (0-40) (<=0 para terminar)? 5
*
***
*****
*******
*********
*
*
*
Quantos ramos (0-40) (<=0 para terminar)? 0
FIM.
Para cada ramo o númeor de espaços será dado por "ramos-i", sendo "i" o númeor do ramo, e o número de asteriscos será dado por "i*2-1". Vejamos este teste no GHCi para entender melhor:
Prelude> map (subtract 1 . (*2)) [1..5]
[1,3,5,7,9]
A lista devolvida pelo GHCi corresponde ao númeor de asteriscos em cada ramo.
Por fim, a posição do tronco será igual a "ramos-1" (é só um asterisco na mesma posição do ramo 1). Neste ramo, i=1, pelo que "ramos-i" será "ramos-1".
Desta forma:
-- espaços no ramo "i"
repeatChar ' ' (ramos-i)
-- asteriscos no ramo "i"
repeatChar '*' (i*2-1)
-- tronco
repeatChar ' ' (ramos-1) ++ "*"
Criámos, por fim, funções que criam estas sequências através de listas por compreensão, localizadas no bloco where
da função arvore
. Assim a leitura da função simplificou imenso.