Programmation fonctionnelle

Partiel - sujet blanc - corrigé

E. Lozes - novembre 2019

Durée : 2h


Exercice 1 - Triangle

Question 1.1 (2 points)

L'aire d'un triangle quelconque de côtés de longueurs $a$, $b$, $c$ est donnée par la formule $\sqrt{p(p-a)(p-b)(p-c)}$ où $p$ désigne le demi-périmètre du triangle. Définissez une fonction aire : float -> float -> float -> float telle que aire a b c est l'aire du triangle de côtés de longueur $a$, $b$, $c$. Vous ne devez pas calculer le demi-périmètre plusieurs fois.

Question 1.2 (2 points)

Définissez une fonction est_rectangle : float -> float -> float -> bool telle que est_rectangle a b c renvoie true si et seulement si le triangle de côtés $a$, $b$, $c$ est un triangle rectangle.

In [4]:
let aire a b c = 
  let p = (a +. b +. c) /. 2. in
  sqrt (p *. (p -. a) *. (p -. b) *. (p -. c))
  
let est_rectangle a b c = 
   (aire a b c) *. (max (max a b) c) = a *. b *. c /. 2.
   
let est_rectangle a b c =
   let [a'; b'; c'] = List.sort compare [a; b; c] in
   a' * a' + b' * b' = c' * c'
Out[4]:
val aire : float -> float -> float -> float = <fun>
Out[4]:
val est_rectangle : float -> float -> float -> bool = <fun>
File "[4]", line 9, characters 3-83:
 9 | ...let [a'; b'; c'] = List.sort compare [a; b; c] in
10 |    a' * a' + b' * b' = c' * c'
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
(_::_::_::_::_|_::_::[]|_::[]|[])
Out[4]:
val est_rectangle : int -> int -> int -> bool = <fun>

Exercice 2 - Plus grand élément d'une liste (4 points)

Définissez de trois façons différentes la fonction max_liste : 'a list -> 'a qui renvoie le plus grand élément d'une liste non vide quelconque (si la liste est vide, la fonction doit lever l'exception Invalid_argument "max_liste"

  1. Utilisez une définition récursive, et précisez en commentaire si elle est récursive terminale ou récursive enveloppée
  2. Utilisez une boucle for ou while
  3. Utilisez List.fold_left

On rappelle l'existence de la fonction max : 'a -> 'a -> 'a.

In [6]:
let rec max_liste = function
  | [] -> invalid_arg "max_liste"
  | [a] -> a
  | a::l -> max a (max_liste l)   (* <- recurrence enveloppee *)
  
let max_liste l = 
  let rec f res = function
    | [] -> res
    | a::l -> f (max a res) l   (* <- récursivité terminale (MIEUX!) *)
  in match l with
    | [] -> invalid_arg "max_liste"
    | a::l -> f a l  
  
let max_liste l =
  match l with
  | [] -> invalid_arg "max_liste"
  | a::l2 -> begin
     let res = ref a in
     let p = ref l2 in
     while !p <> [] do
        res := max !res (List.hd !p);
        p := List.tl !p
     done;
     !res
   end
   
let max_liste l = 
   match l with
   | [] -> invalid_arg "max_liste"
   | a::l2 -> List.fold_left max a l2
Out[6]:
val max_liste : 'a list -> 'a = <fun>
Out[6]:
val max_liste : 'a list -> 'a = <fun>
Out[6]:
val max_liste : 'a list -> 'a = <fun>
Out[6]:
val max_liste : 'a list -> 'a = <fun>

Exercice 3 - Décomposition en facteurs premiers

La décomposition en facteurs premiers (dfp) d'un entier $n\geq 1$ est la suite de ces facteurs premiers listés dans l'ordre croissant. Par exemple

  • la dfp de 140 est la suite 2,2,5,7
  • la dfp de 1 est la suite vide

Question 3.1 (2 points)

Définissez une fonction dfp_vers_entier : int list -> int qui a une décomposition en facteur premier associe l'entier correspondant. Par exemple, dfp_vers_entier [2;2;5;7] renvoie 140.

Question 3.2 (2 points)

Définissez la fonction inverse entier_vers_dfp : int -> int list. Vous pourrez utiliser une boucle for ou while et un style impératif, ou vous pourrez compléter la fonction ci-dessous

let entier_vers_dfp n = 
   let rec f n m = match (n, m) with
      | (1, _) -> []
      | (n, p) when n mod p = 0 -> ...
      | _ -> ....
   in f n ...

Question 3.3 (2 points)

Définissez la fonction dfp_produit : int list -> int list -> int list qui prend les dfp de deux nombres et qui calcule la dfp de leur produit. Par exemple, dfp [2;2;5] [2;3;7] renvoie [2;2;2;3;5;7].

In [7]:
let rec dfp_vers_entier = function
| [] -> 1
| a::l -> a * dfp_vers_entier l

let rec dfp_vers_entier = List.fold_left ( * ) 1

let entier_vers_dfp n = 
  let rec f n m = match (n, m) with
    | (1, _) -> []
    | (n, p) when n mod p = 0 -> p :: (f (n/p) p)
    | _ -> f n (m+1)
  in f n 2
  
let entier_vers_dfp n = 
  let acc = ref [] in
  let m = ref 2 in
  let n = ref n in
  while !n <> 1 do 
     if !n mod !m = 0 
     then begin
        acc := !m :: !acc;
        n := !n / !m
     end else begin
        m := !m + 1
     end
  done;
  List.rev !acc
Out[7]:
val dfp_vers_entier : int list -> int = <fun>
Out[7]:
val dfp_vers_entier : int list -> int = <fun>
Out[7]:
val entier_vers_dfp : int -> int list = <fun>
Out[7]:
val entier_vers_dfp : int -> int list = <fun>

Exercice 4 - Arbres binaires d'expression (3 points)

On rappelle le type des arbres binaires d'expressions vu en cours

In [8]:
type arbre = 
  | Feuille of feuille
  | Noeud of operateur * arbre * arbre
and feuille = 
  | Const of int
  | Var of string
and operateur = PLUS | SUB | MULT | DIV
Out[8]:
type arbre = Feuille of feuille | Noeud of operateur * arbre * arbre
and feuille = Const of int | Var of string
and operateur = PLUS | SUB | MULT | DIV

Question 4.1

Définissez la fonction taille : arbre -> int qui calcule le nombre de noeuds internes d'un arbre (autrement dit le nombre d'opérations.

Question 4.2

Définissez la fonction hauteur : arbre -> int qui calcule la hauteur de l'arbre. On rappelle que la hauteur d'un arbre est longueur du plus long chemin de la racine à une feuille de l'arbre.

Question 4.3

Définissez la fonction variables : arbre -> string list qui calcule la liste des variables apparaissant sur les feuilles de l'arbre.

In [11]:
let rec taille = function
  | Feuille(_) -> 0
  | Noeud(_, arbre1, arbre2) -> taille arbre1 + taille arbre2
  
let rec hauteur = function
  | Feuille(_) -> 0
  | Noeud(_, arbre1, arbre2) -> max (hauteur arbre1) (hauteur arbre2)
  
let rec variables = function
  | Feuille(Var x) -> [x]
  | Feuille(_) -> []
  | Noeud(_, arbre1, arbre2) -> (variables arbre1) @ (variables arbre2)
Out[11]:
val taille : arbre -> int = <fun>
Out[11]:
val hauteur : arbre -> int = <fun>
Out[11]:
val variables : arbre -> string list = <fun>

Exercice 5 - Somme d'un fichier (3 points)

Ecrivez une fonction somme_fichier : string -> int. L'entier somme_fichier nom_fichier renvoyé par la fonction est la somme des lignes de nom_fichier qui contiennent exactement un entier. Les autres lignes sont ignorées. Par exemple, si toto.txt est le fichier

30
il est 8 : 30
20
`

somme_fichier "toto.txt" renverra 50

In [12]:
let somme_fichier fichier =
  let ic = open_in fichier in
  let acc = ref 0 in
  let next_int () = 
    try int_of_string (input_line ic) with
    | Failure(_) -> 0 
  in
  try
    while true do acc := !acc + next_int() done; assert false
  with End_of_file -> close_in ic; !acc
Out[12]:
val somme_fichier : string -> int = <fun>