Fala, pessoal!
Nesse post resolvo um problema da categoria "Estruturas e Bibliotecas". Esses problemas costumam ser um pouco mais difíceis em relação aos da categoria Iniciante, e costumam envolver o uso de alguma estrutura de dados ou biblioteca diferente das padrão. Não é exatamente o caso para resolver ele em Haskell, já que conseguimos definir nossas funções e usar algumas outras que estão disponíveis no módulo padrão 😎
Abaixo você tem acesso a uma das possíveis soluções.
Ah, não esqueça de ajudar o blog!!!
Plataforma: URI (BEECROWD)
Problema: 1022
Enunciado:
In english:
You were invited to do a little job for your Mathematic teacher. The job is to read a Mathematic expression in format of two rational numbers (numerator / denominator) and present the result of the operation. Each operand or operator is separated by a blank space. The input sequence (each line) must respect the following format: number, (‘/’ char), number, operation char (‘/’, ‘*’, ‘+’, ‘-‘), number, (‘/’ char), number. The answer must be presented followed by ‘=’ operator and the simplified answer. If the answer can’t be simplified, it must be repeated after a ‘=’ operator. Considering N1 and D1 as numerator and denominator of the first fraction, follow the orientation about how to do each one of these 4 operations: Sum: (N1*D2 + N2*D1) / (D1*D2) Subtraction: (N1*D2 - N2*D1) / (D1*D2) Multiplication: (N1*N2) / (D1*D2) Division: (N1/D1) / (N2/D2), that means (N1*D2)/(N2*D1)
Linguagem: Haskell
Solução:
A ideia do exercício é realizar os cálculos desejados pelo usuário, que resultarão em uma fração, e então exibir a fração também em formato simplificado. Para isso é necessário saber qual é o maior valor que pode dividir numerador e denominador, ou seja, o máximo divisor comum (mdc).
A estratégia utilizada foi criar várias funções para auxiliar a encontrar a resposta.
A função main apenas lê o valor de n e o passa como parâmetro para a função reading.
Na função reading as expressões são lidas linha a linha e então os números informados são extraídos e colocados em uma lista. Na linha, todas as "palavras" (expressões separadas por espaço) pares são números. Entre os números (palavras ímpares) estão os operadores, sendo que o único operador que importa neste exercício é o do meio, pois o primeiro e o último são sempre "/", garantindo que as entradas são frações.
Para pegar o n-ésimo elemento de uma lista, usa-se nome-lista!!n.
Criei duas funções, getNum e getDen para obter o numerador e o denominador a depender da operação realizada, seguindo as fórmulas do enunciado.
Após obter numerador e denominador, o primeiro valor pode ser impresso. Depois disso, para calcular o número simplificado, basta obter o mdc dos valores e dividir os números obtidos após o primeiro cálculo pelo mdc.
A única condição antes de exibir o valor simplificado é identificar se o numerador obtido no primeiro passo é zero. Isso deve ser feito porque zero no numerador com qualquer denominador (que só não pode ser zero) pode ser simplificado para 0/1.
Veja como ficou o código:
main :: IO () main = do n <- readLn :: IO Int reading n mdc :: Int -> Int -> Int mdc 0 _ = 0 mdc a b | a == b = a | a < b = mdc a (b-a) | otherwise = mdc b (a-b) getNum :: String -> Int -> Int -> Int -> Int -> Int getNum op n1 d1 n2 d2 | op == "+" = n1 * d2 + n2 * d1 | op == "-" = n1 * d2 - n2 * d1 | op == "*" = n1 * n2 | otherwise = n1 * d2 getDen :: String -> Int -> Int -> Int -> Int getDen op n2 d1 d2 | op == "/" = n2 * d1 | otherwise = d1 * d2 reading :: Int -> IO () reading n = do if n == 0 then return () else do line <- getLine let list = words line let [n1, d1, n2, d2] = [read x | (x, i) <- zip list [0..], even i] let op = list!!3 let numFinal = getNum op n1 d1 n2 d2 let denFinal = getDen op n2 d1 d2 if numFinal == 0 then putStrLn ("0/" ++ show (denFinal) ++ " = 0/1") else do let divisor = mdc (abs numFinal) denFinal let numSim = div numFinal divisor let denSim = div denFinal divisor putStrLn (show (numFinal) ++ "/" ++ show (denFinal) ++ " = " ++ show (numSim) ++ "/" ++ show (denSim)) reading (n-1)