Programação Funcional

50 Questões Fichas Testes/Exames

Teste 2020-01-11

Voltar

1. Apresente uma definição recursiva de cada uma das seguintes funções (pré-definidas) sobre listas:

a) intersect :: Eq a => [a] -> [a] -> [a] que retorna a lista resultante de remover da primeira lista os elementos que não pertencem à segunda. Por exemplo, intersect [1,1,2,3,4] [1,3,5] corresponde a [1,1,3].

intersect :: Eq a => [a] -> [a] -> [a]
intersect [] _ = []
intersect (x:xs) ys
    | x `elem` ys = x : intersect xs ys
    | otherwise = intersect xs ys

b) tails :: [a] -> [[a]] que calcula a lista dos sufixos de uma lista. Por exemplo, tails [1,2,3] corresponde a [[1,2,3], [2,3], [3], []].

tails :: [a] -> [[a]]
tails [] = [[]]
tails (x:xs) = (x:xs) : tails xs

2. Para armazenar conjuntos de números inteiros, optou-se pelo uso de sequências de intervalos. Assim, por exemplo, o conjunto (1,2,3,4,7,8,19,21,22,23) poderia ser representado por [(1,4),(7,8),(19,19),(21,23)].

type ConjInt = [Intervalo]
type Intervalo = (Int, Int)

a) Defina uma função elems :: ConjInt -> [Int] que, dado um conjunto, dá como resultado a lista dos elementos desse conjunto.

elems :: ConjInt -> [Int]
elems [] = []
elems ((a,b):t) = [a..b] ++ elems t

b) Defina uma função geraconj :: [Int] -> Conj Int que recebe uma lista de inteiros, ordenada por ordem crescente e sem repetições, e gera um conjunto. Por exemplo, geraconj [1,2,3,4,7,8,19,21,22,23] = [(1,4),(7,8),(19,19),(21,23)].

geraconj :: [Int] -> ConjInt
geraconj [] = []
geraconj (h:t) =
    case geraconj t of 
        [] -> [(h,h)]
        (a,b) : r 
            | a == succ h -> (h,b) : r
            | otherwise -> (h,h) : (a,b) :  r

3. Para armazenar uma agenda de contactos telefónicos e de correio eletrónico definiram-se os seguintes tipos de dados. Não existem nomes repetidos na agenda e para cada nome existe uma lista de contactos.

data Contacto = Casa Integer
              | Trab Integer
              | Tim Integer
              | Email String
    deriving (Show)
type Nome = String
type Agenda = [(Nome, [Contacto])]

a) Defina a função acrescEmail :: Nome -> String -> Agenda -> Agenda que, dado um nome, um email e uma agenda, acrescenta essa informação à agenda.

acrescEmail :: Nome -> String -> Agenda -> Agenda
acrescEmail nome email [] = [(nome, [Email email])]
acrescEmail nome email ((n,c):t) 
    | nome == n = (n, Email email : c) : t
    | otherwise = (n,c) : acrescEmail nome email t

b) Defina a função verEmails :: Nome -> Agenda -> Maybe [String] que, dado um nome e uma agenda, retorna a lista dos emails associados a esse nome. Se esse nome não existir na agenda a função deve retornar Nothing.

verEmails :: Nome -> Agenda -> Maybe [String]
verEmails nome [] = Nothing
verEmails nome ((n,c):t) 
    | nome == n = Just (map (\(Email e) -> e) $ filter isEmail c)
    | otherwise = verEmails nome t
    where isEmail (Email _) = True
          isEmail _ = False

c) Defina a função consulta :: [Contacto] -> ([Integer], [String]) que, dada uma lista de contactos, retorna o par com a lista de números de telefone (tanto telefones fixos como telemóveis) e a lista de emails dessa lista. Implemente a função de modo a fazer uma única travessia da lista de contactos.

consulta :: [Contacto] -> ([Integer], [String])
consulta =
    foldr (\c (tlfs, emails) ->
        case c of 
            Tlm n -> (n:tlfs, emails)
            Casa n -> (n:tlfs, emails)
            Trab n -> (n:tlfs, emails)
            Email e -> (tlfs, e:emails)
        ) ([],[])

d) Defina a função consultaIO :: Agenda -> IO () que, dada uma agenda, lê do teclado o nome que pretende consultar e apresenta no ecrã os contactos associados a esse nome na agenda.

consultaIO :: Agenda -> IO ()
consultaIO a = 
    getLine >>= 
        print 
        . maybe ([],[]) consulta 
        . flip lookup a

4. Relembre o tipo RTree a definido nas aulas.

data RTree a = R a [RTree a] deriving (Show, Eq)

a) Defina a função paths :: RTree a -> [[a]] que, dada uma destas árvores, calcula todos os caminhos desde a raíz até às folhas. Por exemplo, paths (R 1 [R 2 [], R 3 [R 4 [R 5 [], R 6 []]], R 7 []]) deve corresponder à lista [[1,2],[1,3,4,5],[1,3,4,6],[1,7]].

paths :: RTree a -> [[a]]
paths (R a []) = [[a]]
paths (R a t) = map (a:) $ concatMap paths t

b) Defina a função unpaths :: Eq a => [[a]] -> RTree a, inversa da anterior, tal que unpaths (paths t) == t, para qualquer árvore t :: Eq a => RTree a.

import Data.List (groupBy)

unpaths :: Eq a => [[a]] -> RTree a
unpaths [[x]] = R x []
unpaths l = R root (map unpaths $ groupBy (\a b -> head a == head b) $ map tail l)
    where root = head $ head l