Skip to content

Instantly share code, notes, and snippets.

@williaanlopes
Created November 23, 2018 15:59
Show Gist options
  • Save williaanlopes/826fde767567ea98aecf0a0208df1c3f to your computer and use it in GitHub Desktop.
Save williaanlopes/826fde767567ea98aecf0a0208df1c3f to your computer and use it in GitHub Desktop.
Validando CPF com Haskell (Validating CPF with Haskell)
{--
Exemplo de CPF 546.471.429-49
Tutorial no link: https://www.devmedia.com.br/validando-o-cpf-em-uma-aplicacao-java/22097
--}
module Util.CpfValidator where
import Data.Char
{--
Para calcular o 1º dígito verificador
Cada um dos nove primeiros números do CPF é multiplicado por um peso que começa
de 10 e que vai sendo diminuido de 1 a cada passo, somando-se as parcelas calculadas:
# sm = (5*10)+(4*9)+(6*8)+(4*7)+(7*6)+(1*5)+(4*4)+(2*3)+(9*2) = 249;
# Calcula-se o dígito através da seguinte expressão:
11 - (sm % 11) = 11 - (249 % 11) = 11 - 7 = 4
--}
calculoPrimeiroDigitoVerificador :: String -> Int
calculoPrimeiroDigitoVerificador [] = 0
calculoPrimeiroDigitoVerificador (x:xs) = varificaDigito ( 11 - ( mod (somatorioDigitoVerificador (x:xs) 10) 11 ) )
calculoSegundoDigitoVerificador :: String -> Int
calculoSegundoDigitoVerificador [] = 0
calculoSegundoDigitoVerificador (x:xs) = varificaDigito ( 11 - (mod (somatorioDigitoVerificador (x:xs) 11) 11) )
{--
Para calcular o 2º dígito verificador
Cada um dos dez primeiros números do CPF, considerando-se aqui o primeiro DV,
é multiplicado por um peso que começa de 11 e que vai sendo diminuido de 1 a cada passo,
somando-se as parcelas calculadas:
# sm = (5*11)+(4*10)+(6*9)+(4*8)+(7*7)+(1*6)+(4*5)+(2*4)+(9*3)+(4*2) = 299;
# Calcula-se o dígito através da seguinte expressão:
11 - (sm % 11) = 11 - (299 % 11) = 11 - 2 = 9
-}
somatorioDigitoVerificador :: String -> Int -> Int
somatorioDigitoVerificador [] n = 0
somatorioDigitoVerificador (x:xs) n = (digitToInt x * n) + somatorioDigitoVerificador xs (n-1)
{--
Se o resto da divisão em calculoPrimeiroDigitoVerificador ou calculoSegundoDigitoVerificador calculado
for 10 ou 11, o dígito verificador será 0; nos outros casos, o dígito verificador é o próprio resto.
--}
varificaDigito :: Int -> Int
varificaDigito digito
| (digito == 10) || (digito == 11) = 0
| otherwise = digito
{--
Retorna apenas o digito verificador (primeiro ou segundo digito) do CPF
--}
digitoVerificador :: Int -> String -> Int
digitoVerificador _ [] = 0
digitoVerificador n (x:xs)
| n == 1 = digitToInt ( head ( drop 9 (x:xs) ) )
| n == 2 = digitToInt (last (x:xs))
| otherwise = 0
{--
Recebe um cpf FORMATADO e verifica se este eh valido
--}
validarCpf :: String -> Bool
validarCpf [] = False
validarCpf "00000000000" = False
validarCpf "11111111111" = False
validarCpf "22222222222" = False
validarCpf "33333333333" = False
validarCpf "44444444444" = False
validarCpf "55555555555" = False
validarCpf "66666666666" = False
validarCpf "77777777777" = False
validarCpf "88888888888" = False
validarCpf "99999999999" = False
validarCpf (x:xs)
| length (x:xs) < 11 || length (x:xs) > 14 = False -- se cpf for menor que 11 (formado apenas numeros) ou maior que 14 (formato com mascara) eh Falso!
| ( calculoPrimeiroDigitoVerificador ( take 9 (x:xs) ) == (digitoVerificador 1 (x:xs)) )
&& ( calculoSegundoDigitoVerificador ( take 10 (x:xs) ) == (digitoVerificador 2 (x:xs)) ) = True
| otherwise = False
{--
Recebe um cpf NAO FORMATADO e verifica se este eh valido
--}
isCpf :: String -> Bool
isCpf cpf = validarCpf (cpfConverter cpf)
{--
Formata o CPF deixando apenas numeros
--}
cpfConverter :: String -> String
cpfConverter s = filter (/='-') ( filter (/='.') s)
{--
Faz o processo inverso de cpfConverter convertendo o CPF de apenas numeros
para o formato com mascara ex: "###.###.###-##"
--}
cpfFormat :: String -> String
cpfFormat (x:xs)
| (length xs) < 3 = []
| otherwise = take 11 ( (take 3 (x:xs)) ++ "." ++ cpfFormat (drop 3 (x:xs)) ) ++ "-" ++ drop 9 (x:xs)
@williaanlopes
Copy link
Author

Um exemplo de como validar cpf com haskell, ficou um pouco feio, estarei melhorando no futuro... :)

@felipexpert
Copy link

Parabens amigo, otima iniciativa!

@williaanlopes
Copy link
Author

Parabens amigo, otima iniciativa!

Obrigado!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment