Examen Haskell Sujet 01

EXAMEN PROGRAMMATION FONCTIONNELLE : HASKELL | SUJET 01/XX

Examen Haskell

Examinateur : Mr JoëlYk, durée 02 heures

Exercice 20 pts :

1 - Quelle différence fondamentales faites-vous entre paradigme fonctionnel et paradigme impératif ?
2 - Définir les expressions suivantes avec des exemples d’application direct de votre choix:
évaluation paresseuse, instanciation partielle des fonctions .
3 - Donnez les signatures des fonction suivantes : map, unZip, zipWith, foldr et foldl.
4 - Donnez le résultat des expressions suivantes en détaillants toutes les étapes d’appels récursifs :
(a) map (\x → x*x) [3, 2, 1]
(b) foldl (++) [] [[900, 900], [800, 800]]
(c) foldl (+) 0 [50, 40, 30]
(d) foldr (:) [100, 110] [120, 130]
(e) foldr (++) [] [[60, 60], [70, 70]]
(f) foldr (*) 0 [10, 2, 3]
précisions : Ici des appels récursifs clairs et détaillés sont attendus.
Pour les fonctions qui vont suivent, donnez des signatures très larges (types polymorphes)
5 - En utilisant le type MayBe, définir une fonctions maxList qui retourne le plus grand élément d’une liste.
6 – Définir une fonction retElem qui retire l’avant dernier élément d’une liste donnée .
7 - Définir un type Etudiant de la forme {nom::a, matricule::b, ville::c}. Donnez également la signature et le corps des
fonction suivantes : getNom, getMatricule et getVille qui renvoient le nom, le matricule et la ville.
8 – On définit une lise par le type : data List p = Empty | Suite {deb::p, resiList::List p}
(a) Que représente : List p, Empty, Suite, deb, resiList ?
(b) Donnez le type de : Empty, Suite, resiList et deb.
(c) Est-il possible de donner le type de : List p ? Dire pourquoi.
(d) Donnez la valeur [100, 200, 300, 400] dans le type List p.
(e) donnez une fonction sommeList qui somme les éléments d’une telle liste.
(f) Créez des instance de Eq, Ord, Show et Enum pour le type list p.

Découvrez plus d'exercice corriges sur : www.PandaCodeur.com

Correction :

1 - Le paradigme fonctionnel et le paradigme impératif sont deux approches différentes de la programmation. Le paradigme fonctionnel se concentre sur les fonctions et leur composition pour résoudre des problèmes, tandis que le paradigme impératif se concentre sur les instructions et la modification de l'état d'un programme pour atteindre un résultat.

2 - L'évaluation paresseuse est une technique utilisée dans le paradigme fonctionnel pour ne pas évaluer une expression tant que cela n'est pas nécessaire. Cela permet d'économiser des ressources et de travailler avec des structures de données potentiellement infinies. Par exemple, en Haskell, l'expression take 5 [1..] ne va évaluer que les 5 premiers éléments de la liste infinie [1..].

L'instanciation partielle des fonctions est une technique qui permet de créer une nouvelle fonction à partir d'une fonction existante en lui appliquant certains de ses arguments. Par exemple, si f x y = x + y, alors g = f 5 est une nouvelle fonction qui prend un argument y et retourne 5 + y.

3 - Les signatures des fonctions demandées sont :

  • map :: (a -> b) -> [a] -> [b]
  • unZip :: [(a, b)] -> ([a], [b])
  • zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
  • foldr :: (a -> b -> b) -> b -> [a] -> b
  • foldl :: (b -> a -> b) -> b -> [a] -> b

4 - Les résultats des expressions sont :

  • (a) map (\x -> x * x) [3, 2, 1] va appliquer la fonction lambda à chaque élément de la liste [3, 2, 1] et retourner [9, 4, 1].
  • (b) foldl (++) [] [[900, 900], [800, 800]] va concaténer les listes [900, 900] et [800, 800] avec ++, puis concaténer le résultat avec la liste vide []. Le résultat final est [900, 900, 800, 800].
  • (c) foldl (+) 0 [50, 40, 30] va ajouter chaque élément de la liste [50, 40, 30] à l'accumulateur initial 0, pour obtenir 120.
  • (d) foldr (:) [100, 110] [120, 130] va ajouter l'élément 130 à la liste [100, 110] en position de tête, puis ajouter l'élément 120 à la position de tête du résultat précédent. Le résultat final est [120, 130, 100, 110].
  • (e) foldr (++) [] [[60, 60], [70, 70]] va concaténer les listes [60, 60] et [70, 70] avec ++, puis concaténer le résultat avec la liste vide []. Le résultat final est [60, 60, 70, 70].
  • foldr (*) 0 [10, 2, 3] va multiplier chaque élément de la liste `[10, 2, 3]` en partant de la droite (d'où le "r" dans "foldr") en utilisant la valeur initiale 0. 1ère étape : 3 * 0 = 0 2ème étape : 2 * 0 = 0 3ème étape : 10 * 0 = 0,   Le résultat final est donc 0.

Solution plus detaillee :

(a) map (\x → xx) [3, 2, 1]
map (\x → xx) [3, 2, 1]
= (\x → xx) 3 : map (\x → xx) [2, 1]
= 33 : (\x → xx) 2 : map (\x → xx) [1]
= 9 : 22 : (\x → xx) 1 : map (\x → xx) []
= 9 : 4 : 11 : map (\x → xx) []
= 9 : 4 : 1 : []
= [9, 4, 1]

(b) foldl (++) [] [[900, 900], [800, 800]]
foldl (++) [] [[900, 900], [800, 800]]
= foldl (++) ([900, 900]) [[800, 800]]
= foldl (++) ([900, 900]) ([800, 800] ++ [])
= foldl (++) ([900, 900, 800, 800]) []
= [900, 900, 800, 800]

(c) foldl (+) 0 [50, 40, 30]
foldl (+) 0 [50, 40, 30]
= foldl (+) 50 [40, 30]
= foldl (+) 90 [30]
= foldl (+) 120 []
= 120

(d) foldr (:) [100, 110] [120, 130]
foldr (:) [100, 110] [120, 130]
= 120 : foldr (:) [100, 110] [130]
= 120 : 130 : foldr (:) [100, 110] []
= 120 : 130 : [100, 110]
= [120, 130, 100, 110]

(e) foldr (++) [] [[60, 60], [70, 70]]
foldr (++) [] [[60, 60], [70, 70]]
= [60, 60] ++ foldr (++) [] [[70, 70]]
= [60, 60] ++ ([70, 70] ++ foldr (++) [] [])
= [60, 60] ++ [70, 70]
= [60, 60, 70, 70]

(f) foldr () 0 [10, 2, 3]
foldr () 0 [10, 2, 3]
= 10 * foldr () 0 [2, 3]
= 10 * (2 * foldr () 0 [3])
= 10 * (2 * (3 * foldr (*) 0 []))
= 10 * (2 * (3 * 0))
= 0

 

5) maxList :: Ord a => [a] -> Maybe a maxList [] = Nothing maxList xs = Just $ maximum xs

6) retElem :: [a] -> [a] retElem [] = [] retElem [x] = [] retElem (x:y:[]) = [x] retElem (x:xs) = x : retElem xs

7) data Etudiant a b c = Etudiant {nom::a, matricule::b, ville::c}

getNom :: Etudiant a b c -> a getNom (Etudiant nom _ _) = nom

getMatricule :: Etudiant a b c -> b getMatricule (Etudiant _ matricule _) = matricule

getVille :: Etudiant a b c -> c getVille (Etudiant _ _ ville) = ville

8)

a) List p représente une liste dont les éléments sont de type p. Empty représente une liste vide et Suite représente un élément de la liste composé d'une tête (deb) de type p et d'une queue (resiList) de type List p.

(b) Empty est de type List p, Suite est de type p -> List p -> List p, resiList est de type List p et deb est de type p.

(c) Non, il n'est pas possible de donner le type de List p car p est un type polymorphe et n'a pas de contraintes spécifiques.

(d) Suite {deb=100, resiList=Suite {deb=200, resiList=Suite {deb=300, resiList=Suite {deb=400, resiList=Empty}}}} représente [100, 200, 300, 400] dans le type List p.

(e) Voici une implémentation de sommeList pour la liste définie ci-dessus:

sommeList :: Num a => List a -> a
sommeList Empty = 0
sommeList (Suite x xs) = x + sommeList xs

(f) Voici les instances de Eq, Ord, Show et Enum pour le type List p:

instance Eq p => Eq (List p) where
    Empty == Empty = True
    (Suite x xs) == (Suite y ys) = x == y && xs == ys
    _ == _ = False

instance Ord p => Ord (List p) where
    compare Empty Empty = EQ
    compare Empty _ = LT
    compare _ Empty = GT
    compare (Suite x xs) (Suite y ys) =
        case compare x y of
            EQ -> compare xs ys
            LT -> LT
            GT -> GT

instance Show p => Show (List p) where
    show Empty = "[]"
    show (Suite x Empty) = "[" ++ show x ++ "]"
    show (Suite x xs) = "[" ++ show x ++ ", " ++ show xs ++ "]"

instance Enum p => Enum (List p) where
    toEnum n = Suite (toEnum n) Empty
    fromEnum Empty = error "Cannot convert empty list to Enum"
    fromEnum (Suite x xs) = fromEnum x
    succ Empty = error "Cannot get successor of empty list"
    succ (Suite x xs) = Suite (succ x) Empty
    pred Empty = error "Cannot get predecessor of empty list"
    pred (Suite x xs) = Suite (pred x) Empty

Notes Question 6, 7, 8 : Les types polymorphes sont des types qui peuvent prendre n'importe quelle valeur d'un type donné. En Haskell, on utilise généralement des lettres minuscules pour représenter ces types polymorphes. Par exemple, a, b, c, etc. sont des lettres qui peuvent représenter n'importe quel type.

Lorsqu'on donne une signature de type polymorphe, on utilise ces lettres pour représenter les types des arguments et du résultat de la fonction, sans spécifier un type particulier. Par exemple, la signature de la fonction maxList peut être écrite comme maxList :: Ord a => [a] -> Maybe a. Ici, a représente n'importe quel type qui peut être ordonné, et la fonction renvoie un Maybe a pour gérer le cas où la liste est vide.

De même, la signature des fonctions getNom, getMatricule et getVille peut être écrite comme getNom :: Etudiant a b c -> a, getMatricule :: Etudiant a b c -> b et getVille :: Etudiant a b c -> c, respectivement. Ici, Etudiant a b c représente un type qui contient un nom de type a, un matricule de type b et une ville de type c. Les fonctions renvoient simplement le nom, le matricule ou la ville, respectivement.

Enfin, pour la définition de la liste List p, Empty représente une liste vide, Suite {deb::p, resiList::List p} représente une liste non vide, où deb est le premier élément et resiList est le reste de la liste. resiList est également une liste de type List p.

Si vous avez trouvé les eaxamens corrigés en Haskell de Mr JoëlYk intéressants et utiles, pourquoi ne pas les partager avec d'autres personnes qui pourraient également en bénéficier ? Partagez ce lien sur les réseaux sociaux ou envoyez-le à vos amis et collègues. Vous pourriez aider quelqu'un à améliorer ses compétences en programmation ou à trouver des solutions à des problèmes complexes. N'oubliez pas que la connaissance doit être partagée pour grandir. Merci pour votre soutien et votre partage !

Contact WhatsApp : +237 658395978 | Réaliser Par Joël_Yk

  • Aucune note. Soyez le premier à attribuer une note !

Ajouter un commentaire

Anti-spam