Programmation Fonctionnelle, Examen

Version & licenses
Creative Commons License

Programmation Fonctionnelle, Examen

Guyslain Naves

Exercice 1 : Parcoursup.

On demande aux responsables du futur portail Descartes (remplaçant la L1 informatique) de l'UFR Sciences d'Aix-Marseille Université de classer les candidats ayant fait vœux d'y entrer. Les places disponibles seront alors proposées prioritairement aux candidats les mieux classés.

  1. On connait pour chaque candidat son nom, sa date de naissance et ses notes au baccalauréat dans chaque matière (chaque matière est identifiée par une chaîne de caractères). Proposer un type dont les valeurs représentent les candidats.
  2. On voudra enregistrer pour chaque candidat des informations diverses, comme par exemple son classement ou l'état de sa demande. Pour cela, nous devons disposer d'une structure de dictionnaire. Comment construire un type des dictionnaires de candidats (on pourra supposer que les candidats ont des noms distincts) ?
  3. List.sort est la fonction de tri standard sur les listes. Implémenter une fonction de tri ayant le type suivant :

    1. val sort :
    2. by:('elt -> 'comparable)
    3. -> cmp:('comparable -> 'comparable -> int)
    4. -> 'elt list -> 'elt list
  4. Il a été proposé de classer les étudiants selon une note calculée comme suit : trois fois la note de mathématiques plus deux fois celles de physique plus une fois celle de français. Écrire une fonction qui étant donnée une liste d'étudiants, produit la liste des étudiants classés. Pensez à découper votre solution en fonctions simples.

Exercice 2 : File de priorité.

Voici une interface possible de file de priorité.

  1. (* from file PriorityQueue.mli *)
  2. module type S = sig
  3. type priority
  4. type 'elt t

  5. val empty : 'elt t

  6. val push : priority -> 'elt -> 'elt t -> 'elt t
  7. val observe : 'elt t -> (priority * 'elt * 'elt t) option
  8. val pop : 'elt t -> 'elt t

  9. val merge : 'elt t -> 'elt t -> 'elt t
  10. val size : 'elt t -> int

  11. val to_list : 'elt t -> 'elt list
  12. end

On souhaite l'implémenter en utilisant un tas maxiphobique. Ceux-ci s'implémente par un arbre binaire, tel que chaque nœud contient un élément et sa priorité, ainsi que le nombre d'éléments dans le sous-arbre ayant ce nœud pour racine.

  1. Proposer une définition pour le type 'elt t.
  2. Implémenter la fonction size.
  3. On suppose maintenant le reste du module implémenté, sauf to_list. Écrire la fonction to_list, retournant la liste des éléments en ordre de priorité croissante.
  4. Généraliser la fonction to_list en une fonction fold de type :

    1. val fold : ('state -> 'elt -> 'state) -> 'state -> 'elt t -> 'state

    Réécrire to_list en utilisant fold.

  5. Nous souhaitons tester l'implémentation avec QuickCheck. Proposer trois propriétés (des fonctions prenant des arguments arbitraires et devant toujours s'évaluer à true), pour vérifier que size se comporte bien par rapport aux autres opérations. On supposera disposer de générateurs arbitraires d'éléments, de priorités et de files de priorités, de sorte que les arguments des propriétés peuvent être de chacun de ces types. (Ne donner que les propriétés, pas les tests)

Exercice 3 : APB.

L'an dernier, le système d'admission des étudiants, Admission Post-Bac, a été critiqué car les formations en manque de places ont procédé à une sélection par tirage au sort. On s'intéresse ici au problème de tirer au sort $k$ candidats parmi une liste de $n$ candidats.

On trouvera en fin de sujet l'interface d'un module d'aléa similaire à QuickCheck.Gen.

  1. Créer un générateur de booléens paramétré par un flottant $p$ (avec $0 \leq p \leq 1$), qui génère true avec probabilité $p$. Pour cela, générer d'abord un flottant entre 0 et 1 .

    1. val true_with_proba : float -> bool Random.gen
  2. Programmer un générateur qui choisit aléatoirement un élément parmi une liste. Chaque élément doit avoir la même probabilité d'être choisi. (indice : tirer le rang de l'élement choisi).

    1. val choose_one : 'elt list -> 'elt Random.gen
  3. Écrire un générateur de sous-listes d'une liste, gardant chaque élément avec la même probabilité p donnée en argument.

    1. val filter : p:float -> 'elt list -> 'elt list Random.gen
  4. Lors du choix de $k$ éléments parmi $n$, chaque élément a la même probabilité $k/n$ d'être choisi. On propose donc d'utiliser la fonction précédente pour construire une sous-liste. Nous ne pouvons pas garantir que filter retournera une liste avec exactement $k$ éléments. Mais s'il y en a trop, il suffit d'en reprendre $k$ parmi ceux déjà filtrés, et s'il y en manque, il suffit d'en ajouter suffisamment provenant des éléments écartés (récursivement). Écrire les fonctions suivantes :

    1. val bipartition : p:float -> 'elt list -> ('elt list * 'elt list) Random.gen
    2. val choose_k : k:int -> 'elt list -> 'elt list Random.gen
  5. Une autre possibilité est de mélanger les éléments aléatoirement, et de prendre les $k$ premiers. Pour les mélanger, on utilise une approche diviser-pour-régner : on coupe la liste en deux parties aléatoirement, on mélange chaque partie et on les concatène. Implémenter une deuxième version de choose_k.

    1. val shuffle : 'elt list -> 'elt list Random.gen
    2. val choose_k' : k:int -> 'elt list -> 'elt list Random.gen
  6. Les deux précédentes solutions présuppose de connaître la liste par avance, ou au moins sa longueur. On aimerait écrire un algorithme qui reçoit les éléments au fur et à mesure, et peut être arrêté à tout moment pour qu'il donne une solution. (On appelle un tel algorithme online).

    Pour cela on propose de garder une file de priorité contenant au plus $k$ éléments. Chaque élément est inséré avec une priorité choisie aléatoirement. Si la file contient $k+1$ éléments, on supprime le minimum.

    Utiliser la structure de données de l'exercice précédent pour donner une troisième implémentation de choose_k.

    1. val choose_k'' : k:int -> 'elt list -> 'elt list Random.gen

Éléments de langage

Module Random :

  1. (** The type of generators of values of type ['value]. *)
  2. type 'value gen

  3. (** [bool] generates [true] with probability $0.5$. *)
  4. val bool : bool gen

  5. (** [int bound] generates values between [0] and [bound-1]. *)
  6. val int : int -> int gen

  7. (** [float bound] generates values between [0.] and [bound]. *)
  8. val float : float -> float gen

  9. (** [bits] generates values among all the representable integers. *)
  10. val bits : int gen

  11. (** [return v] generates [v] with probability $1$. *)
  12. val return : 'value -> 'value gen

  13. val bind : 'value gen -> ('value -> 'res gen) -> 'res gen
  14. val (>>=) : 'value gen -> ('value -> 'res gen) -> 'res gen

  15. val map : 'value gen -> ('value -> 'res) -> 'res gen
  16. val (>|=) : 'value gen -> ('value -> 'res) -> 'res gen

  17. (** The type of seeds used to start generating random values *)
  18. type seed

  19. val init : seed (** a seed *)

  20. (** Use [run gen seed] to randomly generate a value from [gen].
  21. The same seed will always generate the same value.
  22. It returns a new seed to generate other values.
  23. *)
  24. val run : 'value gen -> seed -> ('value * seed)

Fonctions utiles du module List :

  1. val length : 'elt list -> int
  2. val nth : int -> 'elt list -> 'elt
  3. val rev : 'elt list -> 'elt list
  4. val concat : 'elt list list -> 'elt list
  5. val map : ('elt -> 'im) -> 'elt list -> 'im list
  6. val fold_left : ('state -> 'elt -> 'state) -> 'state -> 'elt list -> 'state
  7. val for_all : ('elt -> bool) -> 'elt list -> bool
  8. val exists : ('elt -> bool) -> 'elt list -> bool
  9. val mem : 'elt -> 'elt list -> bool
  10. val filter : ('elt -> bool) -> 'elt list -> 'elt list
  11. val sort : ('elt -> 'elt -> int) -> 'elt list -> 'elt list

Fonctions utiles de Map.S :

  1. type key
  2. type 'assoc t
  3. val empty : 'assoc t
  4. val add : key -> 'assoc -> 'assoc t -> 'assoc t
  5. val mem : key -> 'assoc t -> bool
  6. val find : key -> 'assoc t -> 'assoc (* may fail if key is not present *)
  7. val map : ('assoc -> 'im) -> 'assoc t -> 'im t
  8. val fold : ('key -> 'assoc -> 'state -> 'state) -> 'assoc t -> 'state -> state
  9. val cardinal : 'assoc t -> int

Exemples de syntaxe :

  1. type ratio = int * int

  2. let rec euclid a b =
  3. if b = 0 then a
  4. else euclid b (a mod b)

  5. let pgcd a b = euclid (abs a) (abs b)

  6. let ratio p q =
  7. let d = pgcd p q in
  8. if q < 0 then (-p / d, q / d)
  9. else (p/d, q/d)

  10. let (+/) (a,b) (c,d) = ratio (a*d + b*c) (b*d)

  11. let (++) (a,b) (c,d) = ratio (a+c) (b+d)

  12. let rec bipartitions list =
  13. let open Base.List in
  14. match list with
  15. | [] -> [([],[])]
  16. | head::tail ->
  17. bipartitions tail >>= fun (left,right) ->
  18. [ (head::left,right); (left, head::right) ]

  19. module Queue =
  20. struct

  21. type 'elt t =
  22. Queue of ('elt list) * ('elt list)

  23. exception Empty

  24. let empty = Queue ([],[])

  25. let is_empty = function
  26. | Queue ([],[]) -> true
  27. | _ -> false

  28. let queue = function
  29. | Queue ([],l) -> Queue(List.rev l,[])
  30. | x -> x

  31. let snoc (Queue (l1,l2)) ele =
  32. queue (Queue (l1,ele::l2))

  33. let head = function
  34. | Queue ([],_) -> raise Empty
  35. | Queue (l,_) -> List.hd l

  36. let tail = function
  37. | Queue ([],_) -> raise Empty
  38. | Queue (l1,l2) -> queue (Queue (List.tl l1,l2))

  39. end