Skip to content

Latest commit

 

History

History
210 lines (175 loc) · 7.61 KB

README.org

File metadata and controls

210 lines (175 loc) · 7.61 KB

TAD Conjunto Inteiros

Este documento se refere ao exercício 3 da LE1, que pode ser encontrado neste documento.

Interface Pública

Essas são as funções expostas por esse módulo

module LE1.Data.TAD
  ( imprimeData
  , converteData
  , somaDias
  , toTuple
  , isVazia
  , vazia
  , isInvalida
  , fromTuple
  , mes
  , ano
  , dia
  , show
  ) where

Note que não haverá implementação explicíta das funções dia, mes e ano, pois as mesmas são definidas automaticamente pelo Haskell quando crio uma nova estrutura com a sintaxe de Record.

Implementação

Esta é a representação de um TAD Data e alguns apelidos para o tipo Int

type Dia = Int
type Mes = Int
type Ano = Int

data Data = Invalida | Vazia |  Data { dia :: Dia
					  , mes :: Mes
					  , ano :: Ano
					  } deriving (Eq, Ord)

Defino que meu TAD Data possui três construtores de tipo: Invalida, Vazia e a própria Data

Essa é a instância da classe de tipo Show, customizada para imprimir um Data formatada

instance Show Data where
  show (Data d m a) = intercalate "/" (map (show) [d, m, a])
  show Invalida     = "Data Inválida"
  show Vazia        = "Data Vazia"

Funções principais

O exercício exige apenas três funções:

Esta função precisa receber uma 3-Tupla ou Tripla de Int, representando respectivamente o dia, mês e ano de uma Data, tenta criar um TAD Data para validar a estrutura e devolve uma String envolvida na Mônada IO que será utilizada pelo programa em CLI para imprimir na tela.

Essa Mônada é responsável por encapsular outros valores e representar que uma operação de Input/Output pode ser executada de forma segura, sem atrapalhar a lógica e andamento do restante do algoritmo.

imprimeData :: (Int, Int, Int) -> IO String
imprimeData (d, m ,a) = (case data' of
		          Invalida -> return "Invalida"
		          Vazia    -> return "Estrutura vazia"
		          Data {}  -> return dataValida)
			   where data'      = criaData (d, m, a)
			         dataValida =  (printf "%d/%d/%d" d m a) :: String

Dado uma String no formato dd/mm/AAAA, tento criar uma Data validada e retorno essa estrutura. Uso fortemente a estrutura de controle case ... of que, por correspondência de valores, retorna determinado valor caso o argumento dê match com uma das opções

converteData :: String -> Data -> Data
converteData [] d           = d
converteData _ (Data _ _ _) = Invalida
converteData _ Invalida     = Invalida
converteData d Vazia        = (case criaData (d', m, a) of
				 Invalida -> Invalida
				 Vazia    -> Vazia
				 Data {}  -> Data {dia = d', mes = m, ano = a})
				   where d':m:a:_ = map (\x -> read x :: Int) (separa (=='/') d)

Defino a função de somar dias de uma Data recursivamente. O caso base é quando os dias a serem somados são 0. Se o dia e mês da data forem 1 e os dias a serem somados forem maior que o tamanho do ano da Data, aumento o ano e chamo novamente a função com os dias a serem somados menos o tamanho do ano da Data Para qualquer outra Data, faço a seguinte verificação:

Sendo x os dias a serem somados, temos que

  1. se x é negativo? -> Data Invalida
  2. se x >= os dias restantes do ano -> ano + 1 e x = x - tamanho do ano
  3. se x >= os dias restantes no mes ->
    se o mes == 12, aumento o ano, senão aumento o mes e x = x - dias restantes do mes
  4. caso genérico -> retorno uma Data com dias somados a x
somaDias :: Data -> Int -> Data
somaDias data' 0 = data'
somaDias (Data 1 1 y) d'
	| d' < 0         = Invalida
	| d' >= tamAno y = somaDias (Data 1 1 (y + 1)) (d' - tamAno y)
somaDias data'@(Data d m y) d'
	| d' < 0                 = Invalida
	| d' >= faltaEmAno data' = somaDias (Data 1 1 (y + 1)) (d' - faltaEmAno data')
	| d' >= faltaEmMes data' = somaDias (if m == 12 then Data 1 1 (y + 1) else Data 1 (m + 1) y) (d' - faltaEmMes data')
	| otherwise              = Data (d + d') m y
somaDias Invalida _ = Invalida
somaDias Vazia d    = somaDias (Data 1 1 2021) d

Funções de ajuda

Funções básicas para ter compatibilidade entre a estrutura de dados Tupla e fazer algumas verificações

vazia :: Data
vazia = Vazia

isInvalida :: Data -> Bool
isInvalida Invalida = True
isInvalida _        = False

isVazia :: Data -> Bool
isVazia Vazia = True
isVazia _     = False

toTuple :: Data -> Maybe (Int, Int, Int)
toTuple (Data d m a) = Just (d, m, a)
toTuple _            = Nothing

fromTuple :: (Int, Int, Int) -> Data
fromTuple d = criaData d

Apenas crio um TAD Data válido, caso os argumentos passem nas validações definidas por mim. Caso contrário retorno uma Data inválida

criaData :: (Int, Int, Int) -> Data
criaData (d, m, a)
	| d < 1 || d > 31      = Invalida
	| m < 1 || m > 12      = Invalida
	| a < 1920 || a > 2021 = Invalida
	| m == 2 && d > 29     = Invalida
	| otherwise            = Data {dia = d, mes = m, ano = a}

Funções para fazer pequenas contas com dias.

Verifico se um ano é bissexto, que me permite calcular quantos dias um ano vai ter;

Dado um mês e um ano, devolvo a quantidade de dias num mês, que é representado pelo valor correspondente ao índice do número do mês na lista definida;

Calculo quantos dias faltam num mês, diminuindo os dias fornecidos como argumento da quantidade de dias no mês mais um;

Para achar há quantos dias um ano começou, basta somar a quantidade de dias dos meses passados com a quantidade de dias do mês atual menos um;

Finalmente, diminuo do tamanho total (em dias) de um ano, os dias que já passaram, obtendo quantos dias ainda faltam para aquele ano acabar

anoBissexto :: Int -> Bool
anoBissexto n = (mod) n 4 == 0 && ((mod) n 100 /= 0 || (mod) n 400 == 0)

tamAno :: Int -> Int
tamAno n = if anoBissexto n then 366 else 365

-- De forma "imperativa", pego quantos dias tem um mês
tamMes :: Int -> Int -> Int
tamMes a' m' = meses !! (m' - 1) where
	meses   = if anoBissexto a' then meses'' else meses'
	meses'  = [31,28,31,30,31,30,31,31,30,31,30,31]
	meses'' = [31,29,31,30,31,30,31,31,30,31,30,31]

faltaEmMes :: Data -> Int
faltaEmMes Invalida     = -1
faltaEmMes Vazia        = 0
faltaEmMes (Data d m y) = tamMes y m - d + 1

diasInicioAno :: Data -> Int
diasInicioAno Invalida     = -1
diasInicioAno Vazia        = 0
diasInicioAno (Data d m y) = mesesAnterioriores + d - 1 where
	mesesAnterioriores = sum [tamMes y m' | m' <- deleta m [1..m]]

faltaEmAno :: Data -> Int
faltaEmAno data' = tamAno (ano data') - inicio
	where inicio = diasInicioAno data'

Funções para manipular listas.

A primeira, recebe como argumento uma função Char -> Bool, exemplo: (== '!'), e uma String; devolve a String separada pelo delimitador em forma de lista

A segunda apenas remove um dado elemento de uma lista, retornando outra lista sem tal elemento

separa :: (Char -> Bool) -> String -> [String]
separa p s = case dropWhile p s of
	      "" -> []
	      s' -> w : separa p s''
		where (w, s'') = break p s'

deleta :: Eq a => a -> [a] -> [a]
deleta deleted xs = [ x | x <- xs, x /= deleted ]