Free Palestine and Lebanon 🍉 Stop the Genocide
Haskell Logo

Programação Funcional

Teste 2021-01-20

Voltar

1. Apresente uma definição recursiva da função (\\\\) :: Eq a => [a] -> [a] -> [a] que retorna a lista resultante de remover (as primeiras ocorrências) dos elementos da segunda lista da primeira. Por exemplo, (\\) [1,2,3,4,5,1,2] [2,3,4,1,2] == [5,1].

(\\) :: Eq a => [a] -> [a] -> [a]
(\\) [] _ = []
(\\) l [] = l
(\\) l (h:t) = (\\) (delete h l) t
    where
        delete :: Eq a => a -> [a] -> [a]
        delete _ [] = []
        delete x (h:t)
            | x == h = t
            | otherwise = h : delete x t

2. Considere o tipo MSet a para representar multi-conjuntos de elementos de a: type MSet a = [(a,Int)]. Considere ainda que nestas listas não há pares cuja primeira componente coincida, nem cuja segunda componente seja menor ou igual a zero.

a) Defina a função removeMSet :: Eq a => a -> [(a,Int)] -> ((a,Int)] que remove um elemento a um multi-conjunto. Se o elemento não existir, deve ser retornado o multi-conjunto recebido. Por exemplo, removeMSet 'c' [('b',2), ('a',4), ('c',1)] == [('b',2),('a',4)].

removeMSet :: Eq a => a -> MSet a -> MSet a
removeMSet _ [] = []
removeMSet x ((a,n):t)
    | x == a = if n == 1 then t else (a,n-1) : t
    | otherwise = (a,n) : removeMSet x t

b) Usando uma função de ordem superior, defina a função calcula :: MSet a -> ([a],Int) que, numa única travessia do multi-conjunto, calcula simulanemente a lista (sem repetidos) de elementos do multi-conjunto e o número total de elementos. Por exemplo, calcula [('b',2), ('a',4), ('c',1)] == (['b','a','c'], 7).

calcula :: MSet a -> ([a],Int)
calcula = foldr (\(a,n) (elems, total) -> (a : elems, n + total)) ([],0)

3. Defina a função partes :: String -> Char -> [String] que parte uma string pelos pontos onde um dado caracter ocorre. Por exemplo, partes "um;bom;exemplo;" ';' == ["um","bom","exemplo"] e partes "um;exemplo;qualquer" ';' == ["um","exemplo",'"qualquer"].

partes :: String -> Char -> [String]
partes "" _ = []
partes s delim = 
    case span (/= delim) s of
        ("",rest) -> partes (tail rest) delim
        (part,"") -> [part]
        (part,rest) -> part : partes (tail rest) delim

4. Considere o seguinte tipo para representar árvores binárias de procura

data BTree a = Empty | Node a (BTree a) (BTree a)

a1 = Node 5 (Node 3 Empty Empty)
            (Node 7 Empty (Node 9 Empty Empty))

a) Defina a função remove :: Ord a => a -> BTree a -> BTree a que remove um elemento de uma árvore binária de procura.

remove :: Ord a => a -> BTree a -> BTree a
remove _ Empty = Empty
remove x (Node a l r)
    | x > a = Node a l (remove x r)
    | x < a = Node a (remove x l) r
    | otherwise = 
        case (l,r) of 
            (Empty, r) -> r
            (l, Empty) -> l
            (l, r) -> let (min, sMin) = minSMin r in Node min l sMin

minSMin :: BTree a -> (a, BTree a)
minSMin (Node e Empty r) = (e, r)
minSMin (Node e l r) = let (min, sMin) = minSMin l in (min, Node e sMin r)

b) Defina BTree a como uma instância da classe Show de forma a que show a1 produza a string ((* <-3-> *) <-5-> (* <-7-> (* <-9-> *)))

instance Show a => Show (BTree a) where
    show :: Show a => BTree a -> String
    show Empty = "*"
    show (Node e l r) = "(" ++ show l ++ " <-" ++ show e ++ "-> " ++ show r ++ ")"

5. Apresente uma definição da função sortOn :: Ord b => (a -> b) -> [a] -> [a] que ordena uma lista comparando os resultados de aplicar uma função de extração de uma chave a cada elemento de uma lista. Por exemplo, sortOn snd [(3,1),(2,5),(1,2)] == [(3,1),(1,2),(2,5)].

sortOn :: Ord b => (a -> b) -> [a] -> [a]
sortOn _ [] = []
sortOn f (h:t) = sortOn f smaller ++ [h] ++ sortOn f larger
    where pivot = f h
          (smaller, larger) = foldr (\x (s,l) -> 
            if f x < pivot then
                (x : s, l)
            else
                (s, x : l)
            ) ([],[]) t in

6. Considere o seguinte tipo para representar um sistema hierárquico de ficheiros

data FileSystem = File Nome | Dir Nome [FileSystem]
type Nome = String

fs1 = Dir "usr" [Dir "xxx" [File "abc.txt", File "readme", Dir "PF" [File "exemplo.hs"]],
                 Dir "yyy" [], Dir "zzz" [Dir "tmp" [], File "teste.c"] ]

a) Defina a função fichs :: FileSystem -> [Nome] que lista o nome de todos os ficheiros de um file system.

fichs :: FileSystem -> [Nome]
fichs (File nome) = [nome]
fichs (Dir _ files) = concatMap fichs files

b) Defina a função dirFiles :: FileSystem -> [Nome] -> Maybe [Nome] que lista o nome dos ficheiros de um file system que estão numa determinada path. Se a path não for válida, a função deve devolver Nothing. Por exemplo, dirFiles fs1 ["usr","xxx"] == Just ["abc.txt","readme"].

dirFiles :: FileSystem -> [Nome] -> Maybe [Nome]
dirFiles (File name) [] = Just [name]
dirFiles (Dir name files) (h:t)
    | h == name = 
        let results = mapMaybe (`dirFiles` t) files in
            if null results then
                Nothing
            else
                Just $ concat results
    | otherwise = Nothing
dirFiles _ _ = Nothing

c) Defina a função listaFich :: FileSystem -> IO () que lê uma path do teclado e imprime no ecrã os nomes dos ficheiros que estão na diretoria indicada pela path. A path deve ser lida como uma string com o formato usual (por exemplo: “usr/xxx/PF”). Se a path não for válida, deve ser escrita a mensagem “Não é uma diretoria.”.

listaFich :: FileSystem -> IO ()
listaFich fs = do
    putStr "> "
    path <- getLine
    case dirFiles fs (partes path '/') of 
        Just files -> print files
        Nothing -> putStrLn "Não é uma diretoria."