Pesquisar este blog

Livros Recomendados

quarta-feira, 1 de fevereiro de 2023

Factorial (Fatorial) - Math - Matemática - Haskell

Fala, pessoal!


Hoje vim aqui postar sobre o problema do Fatorial. Este problema não está associado a nenhuma página de juíz online ou a qualquer desafio específico de maratona de programação e afins, mas se propõe a solucionar um problema clássico de programação (e por que não, da matemática também) em uma linguagem não tão popular como C/C#/C++, Java, Python e outras. Vamos solucioná-lo em Haskell!

Problema

Fatorial é um cálculo realizado com números naturais, isto é, inteiros não negativos, realizando-se o produtório de k, partindo de 1 e indo até n. Assim, o fatorial de 3 seria 3 x 2 x 1 = 6. Representa-se o fatorial de n como n!.

O caso base do fatorial é 0! = 1, ou seja, o fatorial de 0 é 1. Entendido? Abaixo indico os resultados esperados para o fatorial dos primeiros dez números naturais:

  • 0! = 1
  • 1! = 1
  • 2! = 2
  • 3! = 6
  • 4! = 24
  • 5! = 120
  • 6! = 720
  • 7! = 5040
  • 8! = 40320
  • 9! = 362880

Enunciado:

Declare a função fatorial em Haskell.

Linguagem: Haskell

Soluções:

Apresento aqui oito soluções, cada uma feita de uma forma diferente, mas todas resolvem o problema do fatorial.

As quatro primeiras soluções são simples e não tratam entradas negativas. Considerando que somente valores naturais serão inseridos, elas estão corretas.

A função fatorial1 realiza o produto dos valores entre 1 e n por meio da função product, que recebe uma lista de valores. Esses valores são 1..n, ou seja, 1, 2, 3, 4, ..., n. Caso o valor informado seja zero, o resultado será 1 e também estará correto. O problema acontecerá se o valor informado for negativo, caso em que o valor 1 é retornado. O tipo foi definido como Integer -> Integer, mas poderia ter sido definido genericamente como (Num a, Enum a) => a -> a.

A função fatorial2 utiliza guardas para declarar cada caso do fatorial. Sem tratar valores negativos há somente dois casos, o caso base (n==0) e o caso contrário (definido com a palavra reservada otherwise), que é quando ocorre a recursão multiplicada por n, ou, em outras palavras, n * fatorial2(n-1). O tipo foi definido como Integer -> Integer, mas poderia ter sido utilizado (Eq b, Num b) => b -> b.

A função fatorial3 define os casos sem usar guardas, sendo o caso base fatorial3 0 = 1 e o outro caso fatorial3 n = n * fatorial3(n-1). O tipo foi definido como Integer -> Integer.

A função fatorial4 é definida em uma linha utilizando a estrutura condicional if. No if, é testado se o valor de n é zero (caso base), caso em que se retorna 1, senão (else) retorna-se n * fatorial4 (n-1). O tipo foi definido como Integer -> Integer.

A função fatorial5 trata valores negativos utilizando o tipo Either. O tipo Either tem dois tipos possíveis e ele pode assumir qualquer um deles. O primeiro tipo é o tipo Left (da esquerda) e o segundo é o tipo Right (da direita). Neste caso, quando n for menor que zero, usa-se o tipo Left, definido como String para que uma mensagem seja exibida ao usuário. Se não for, o tipo da direita (Right) é escolhido, que neste caso será o tipo Integer para que se proceda o cálculo do fatorial através da definição de fat, que é realizada no bloco where. A função fatorial5 é do tipo fatorial5 :: Integer -> Either String Integer

A função fatorial6 é exatamente igual à função fatorial2, mas contém uma guarda a mais para tratar o caso de n < 0. Neste caso, exibe uma mensagem como um erro (error). Se um valor negativo for informado, ocorre uma exceção e a mensagem definida em "error" é exibida. O tipo foi definido como Integer -> Integer.

A função fatorial7, assim como ocorre com a função fatorial5, usa o tipo Data.Either. Nesta função a diferença é que a recursão é definida no bloco case e o caso base é definido na função externa, com Right 1. O tipo foi definido como Integer -> Either String Integer.

A função fatorial8 utiliza o tipo Maybe. O Maybe, que signfica talvez, é um tipo que pode assumir o valor informado após Maybe. Caso o valor assumido não seja deste tipo informado, ele será vazio, e assim diz-se que ele é do tipo Nothing (nada). Sabendo disso, o caso de valores negativos resultará em Nothing, e os casos em que valores naturais são informados serão calculados conforme a definição do fatorial, 0! = 1 e n! = n * fatorial(n-1) para valores maiores que zero. O tipo foi definido como Integer -> Maybe Integer.

Códigos em Haskell:

-- Arquivo respostas.hs

--fatorial1 :: (Num a, Enum a) => a -> a
fatorial1 :: Integer -> Integer
fatorial1 n = product [1..n]

--fatorial2 :: (Eq b, Num b) => b -> b
fatorial2 :: Integer -> Integer
fatorial2 n
   | n == 0 = 1
   | otherwise = n * fatorial2 (n-1)

fatorial3 :: Integer -> Integer
fatorial3 0 = 1
fatorial3 n = n * fatorial3 (n-1)

fatorial4 :: Integer -> Integer
fatorial4 n = if n==0 then 1 else n * fatorial4 (n-1)

fatorial5 :: Integer -> Either String Integer
fatorial5 n
  | n < 0 = Left "A entrada informada deve ser um valor inteiro nao negativo."
  | otherwise = Right (fat n)
  where fat 0 = 1
        fat n = n * fat (n - 1)

fatorial6 :: Integer -> Integer
fatorial6 n 
  | n < 0 = error "A entrada informada deve ser um valor inteiro nao negativo."
  | n == 0 = 1
  | otherwise = n * fatorial6 (n-1)

fatorial7 :: Integer -> Either String Integer
fatorial7 n
  | n < 0 = Left "A entrada informada deve ser um valor inteiro nao negativo."
  | n == 0 = Right 1
  | otherwise = case fatorial7 (n - 1) of
                  Left s -> Left s
                  Right x -> Right (n * x)

fatorial8 :: Integer -> Maybe Integer
fatorial8 n
  | n < 0 = Nothing
  | n == 0 = Just 1
  | otherwise = do
      resultado <- fatorial8 (n - 1)
      return (n * resultado)

E aí, o que acharam? Há diversas outras soluções e incentivo todas e todos a comentarem este post com as suas propostas de solução!

Uma observação que julgo importante é que na data de hoje eu solicitei uma Inteligência Artificial (ChatGPT) a criação de códigos fatorial e algumas soluções informadas NÃO estavam corretas! Portanto, minha sugestão é sempre exercitar a lógica, estudar a documentação e testar exaustivamente os códigos. É assim que evoluímos!

Por hoje era isso, um abraço e bons estudos!

Nenhum comentário:

Postar um comentário

Postagem em destaque

URI (BEECROWD) - 2158 - Helping Uncle Cláudio (Ajudando o Tio Cláudio) - Matemática - C, C++ e Haskell

Buenas! Estou aqui mais uma vez para resolver um problema de Matemática! Agora tenho resolvido alguns dessa categoria, pra que vocês possam ...

Postagens mais visitadas