Programmation fonctionnelle

Corrigé du partiel du 4 novembre 2019

E. Lozes - Université Nice Sophia Antipolis


Exercice 1 - Un exercice relativement facile

Définissez une fonction einstein u v qui prend en argument deux vitesses et qui calcule leur somme avec la loi d'addition des vitesses en relativité restreinte.

$$ \mathsf{einstein}(u,v) = \frac{u+v}{1+\frac{uv}{c^2}} $$

où la vitesse de la lumière $c= 300000 km.s^{-1}$ est une constante que vous définirez en dehors de votre fonction.

Le type de la fonction doit être float -> float -> float

In [2]:
let c = 300000.
let einstein u v = (u +. v) /. (1. +. (u *. v) /. (c ** 2.))
Out[2]:
val c : float = 300000.
Out[2]:
val einstein : float -> float -> float = <fun>

Exercice 2 - Aplatissement de liste

Définissez de quatre façons différentes la fonction flatten : 'a list list -> 'a list telle que flatten [l1; ... ln] renvoie la concaténation l1@...@ln. Par exemple, flatten [[1;2];[];[3]] renvoie [1;2;3].

  1. Utilisez une définition récursive "naturelle" mais enveloppée
  2. Utilisez une définition récursive terminale
  3. Utilisez une boucle for ou while
  4. Utilisez List.fold_left
In [1]:
(* 1. definition recursive "naturelle" *)
let rec flatten = function
| [] -> []
| l::ll -> l @ (flatten ll)

(* 2. definition recursive terminale *)
let flatten ll =
  let rec f res = function
  | [] -> res
  | l::ll -> f (res @ l) ll
  in f [] ll

(* 2' (avance). definition recursive terminale en temps lineaire (non demande) *)
let flatten ll =
  let rec f res = function
  | [] -> res
  | []::ll -> f res ll
  | (x::l)::ll -> f (x::res) (l::ll)
  in f [] (List.rev (List.map List.rev ll))


(* 3. imperatif *)
let flatten ll = 
  let res = ref [] in
  let p = ref ll in
  while !p <> [] do
     res := !res @ List.hd !p;
     p := List.tl !p
  done;
  !res

(* 4. avec fold_left *)
let flatten ll = List.fold_left ( @ ) [] ll

(* ou encore *)
let flatten = List.fold_left ( @ ) []

(* 4' avec fold_left en temps linéaire (avancé) *)
let flatten ll = ll |> List.fold_left List.rev_append [] |> List.rev
Out[1]:
val flatten : 'a list list -> 'a list = <fun>
Out[1]:
val flatten : 'a list list -> 'a list = <fun>
Out[1]:
val flatten : 'a list list -> 'a list = <fun>
Out[1]:
val flatten : 'a list list -> 'a list = <fun>
Out[1]:
val flatten : 'a list list -> 'a list = <fun>
Out[1]:
val flatten : '_weak1 list list -> '_weak1 list = <fun>
Out[1]:
val flatten : 'a list list -> 'a list = <fun>

Exercice 3 - Quadratfrei

Un nombre entier $n\geq 1$ est dit quadratfrei s'il n'existe pas de nombre $m\geq 2$ tel que $m^2$ divise $n$. Par exemple

  • $1$ est un quadratfrei
  • tout nombre premier est quadratfrei
  • $30=2\times3\times 5$ est quadratfrei
  • 4,9,12,18,... ne sont pas quadratfrei

Définissez une fonction est_quadratfrei : int -> bool qui permet de tester si un entier $n\geq 1$ est quadratfrei.

In [17]:
let est_quadratfrei n =
  let rec iter = function 
  | m when m * m > n -> true
  | m when n mod (m * m) = 0 -> false
  | m -> iter (m+1)
  in iter 2

let est_quadratfrei n = 
  let m = ref 2 in
  while !m * !m <= n && n mod !m * !m <> 0 do
    m := !m + 1
  done;
  !m * !m > n
Out[17]:
val est_quadratfrei : int -> bool = <fun>
Out[17]:
val est_quadratfrei : int -> bool = <fun>

Exercice 4 - Module d'un nombre complexe

On veut définir un type complexe qui permet de représenter un nombre complexe soit "en scalaire", avec sa partie réelle et sa partie imaginaire, soit "en polaire", avec son module et son argument. On introduit les types de données suivants.

type scalaire = {re:float; im:float}
type polaire = {mo:float; arg:float}
type complexe = 
| Scalaire of scalaire
| Polaire of polaire
  1. Définissez la fonction module_p : polaire -> float qui calcule le module d'un enregistrement de type polaire, autrement dit le contenu du champs mo.

  2. Définissez la fonction module_s : scalaire -> float qui calcule le module d'un enregistrement de type scalaire. On rappelle que si $x$ et $y$ sont les parties réelle et imaginaire d'un nombre complexe, alors son module est égal à $\sqrt{x^2+y^2}$.

  3. Définissez la fonction module_c : complexe -> float qui calcule le module d'un nombre complexe, représenté soit par ses coordonnées scalaires, soit par ses coordonnées polaires.

In [2]:
type scalaire = {re:float; im:float}
type polaire = {mo:float; arg:float}
type complexe = 
| Scalaire of scalaire
| Polaire of polaire

let module_p pol = pol.mo
let module_p {mo} = mo

let module_s sca = sqrt (sca.re ** 2. +. sca.im ** 2.)
let module_s {re=x;im=y} = sqrt (x ** 2. +. y ** 2.)

let module_c = function
| Scalaire(sca) -> module_s sca
| Polaire(pol) -> module_p pol
Out[2]:
type scalaire = { re : float; im : float; }
Out[2]:
type polaire = { mo : float; arg : float; }
Out[2]:
type complexe = Scalaire of scalaire | Polaire of polaire
Out[2]:
val module_p : polaire -> float = <fun>
Out[2]:
val module_p : polaire -> float = <fun>
Out[2]:
val module_s : scalaire -> float = <fun>
Out[2]:
val module_s : scalaire -> float = <fun>
Out[2]:
val module_c : complexe -> float = <fun>

Exercice 5 - Arbres binaires d'expression

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

In [19]:
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[19]:
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 5.1

Définissez la fonction nb_feuilles : arbre -> int qui calcule le nombre de feuilles d'un arbre.

Question 5.2

Définissez la fonction sans_div : arbre -> bool qui renvoie true si l'opérateur DIV n'apparait pas dans l'arbre.

In [21]:
let rec nb_feuilles = function
| Feuille(_) -> 1
| Noeud(_, arbre1, arbre2) -> (nb_feuilles arbre1) + (nb_feuilles arbre2)

let rec sans_div = function
| Feuille(_) -> true
| Noeud(DIV, _, _) -> false
| Noeud(_, arbre1, arbre2) -> sans_div arbre1 && sans_div arbre2
Out[21]:
val nb_feuilles : arbre -> int = <fun>
Out[21]:
val sans_div : arbre -> bool = <fun>

Exercice 6 - Nombre de lignes d'un fichier

Ecrivez une fonction wc : string -> int. L'entier wc nom_fichier renvoyé par la fonction est le nombre de lignes du fichier nom_fichier.

Remarque (souvenir de L1?) : le nom de la fonction est tiré de l'utilitaire shell wc (word count) qui fait la même chose avec l'option -l.

In [23]:
let wc fichier = 
  let ic = open_in fichier in
  let res = ref 0 in
  try
     while true do
        ignore (input_line ic);
        res := !res + 1
     done;
     assert false
  with
     End_of_file -> close_in ic; !res
     
(* autre solution *)

let wc fichier = 
  let ic = open_in fichier in
  let iter n = 
    try input_line ic; iter (n+1) 
    with End_of_file -> close_in ic; n
  in iter 0
Out[23]:
val wc : string -> int = <fun>

Mémo

In [24]:
sqrt 2. +. 3. ** 2.
Out[24]:
- : float = 10.4142135623730958
In [26]:
let pi = 4. *. atan 1.
Out[26]:
val pi : float = 3.14159265358979312

List.fold_left (++) x0 [x1;...;xn] renvoie (...(x0++x1)++...)++xn.

In [4]:
List.fold_left (^) "" ["hello"; " "; "world"; "!"]
Out[4]:
- : string = "hello world!"
In [7]:
List.hd [1;2;3]
Out[7]:
- : int = 1
In [9]:
List.tl [1;2;3]
Out[9]:
- : int list = [2; 3]
In [11]:
let r = ref 0
Out[11]:
val r : int ref = {contents = 0}
In [18]:
while !r <> 10 do r := !r + 1; print_int !r done
Out[18]:
- : unit = ()
In [20]:
int_of_string "23"
Out[20]:
- : int = 23
In [21]:
int_of_string "23 : 30"
Exception: Failure "int_of_string".
Raised by primitive operation at unknown location
Called from file "toplevel/toploop.ml", line 208, characters 17-27
In [15]:
let premiere_ligne fichier = 
  let ic = open_in fichier in
  let res = input_line ic in
  close_in ic;
  res
Out[15]:
val premiere_ligne : string -> string = <fun>