I. Rappels¶
- Types de base :
int
,float
,char
,string
,unit
- Opérateurs : ...
- Fonctions prédéfinies
- Types des fonctions ;
t_arg1 -> t_arg2 ... -> t_res
- Application (appel de fonction) : sans parenthèses
- Priorité de l'appel de fonction sur les autres opérateurs
Rappels - suite¶
- Variable globale, locale
- Fonction globale, locale
- n paramètres, appel de fonction
- "0" paramètre = 1 paramètre de type
unit
:let test () = ...;;
test ();;
- Typage automatique (inférence de type) : c'est le type le plus général qui est conservé
- Variable de type (polymorphisme) :
'a
II. Fonction récursive¶
on utilise le mot clé rec
let rec f arg1 arg2 ... argn = expr
: fonction récursive globalelet rec f arg1 arg2 ... argn = expr1 in expr2
: fonction récursive locale
III. Expression conditionnelle¶
if cond then expr1 else expr2
- Contraintes de typage ?
cond
est de typebool
expr1
etexpr2
sont de même type
Exemple :¶
Définir la fonction (récursive) puissance_entiere
de type int -> int -> int
telle que puissance_entiere m n
calcule $m ^n$
let rec puissance_entiere m n = if n = 0 then 1 else m * puissance_entiere m (n-1);;
puissance_entiere 2 10;;
val puissance_entiere : int -> int -> int = <fun>
- : int = 1024
IV. Exercices¶
Exercice 1 :¶
- Donner le type et valeur des expressions suivantes, ou erreur le cas échéant
let plus_un x = x + 1;;
let plus_un x = x + 1;;
val plus_un : int -> int = <fun>
plus_un 7;;
plus_un 7;;
- : int = 8
let apply f x = f x;;
let apply f x = f x;;
val apply : ('a -> 'b) -> 'a -> 'b = <fun>
La fonction apply
a 2 paramètres.
Le premier f
est une fonction : a -> b
Le second x
est passé en paramètre à f
: a
Le résultat f x
est donc de type b
apply plus_un 4;;
apply plus_un 4;;
- : int = 5
let plus_un x = x + 1;;
let apply f x = f x;;
val plus_un : int -> int = <fun>
val apply : ('a -> 'b) -> 'a -> 'b = <fun>
(a -> b) -> a -> b
avec a = int
et b = int
apply plus_un "4";;
apply plus_un "4";; (* types incorrects : int->int string*)
File "[6]", line 1, characters 14-17: 1 | apply plus_un "4";; (* types incorrects : int->int string*) ^^^ Error: This expression has type string but an expression was expected of type int
si le premier paramètre d'apply
est de type int -> ...
, le second paramètre doit être de type int
let double_apply f x = f (f x);;
let double_apply f x = f (f x);;
val double_apply : ('a -> 'a) -> 'a -> 'a = <fun>
Le résutat de f x
doit être de même type que le type du paramètre de f
.
Donc f : a -> a
double_apply plus_un 4;;
double_apply plus_un 4;;
- : int = 6
Exercice 2 :¶
Définir la fonction
maximum a b
qui renvoie la plus grande valeur des deux argumentsQuel est le type de cette fonction ?
Tester sur différents arguments, de différents types
NB : cette fonction est prédéfinie en OCaml : c'est la fonction max
let maximum a b = if a > b then a else b;; (* fonction polymorphe *)
let maximum a b = if a > b then a else b;; (* fonction polymorphe *)
val maximum : 'a -> 'a -> 'a = <fun>
maximum 17 8;; (* deux entiers *)
maximum 1.05 1.06;; (* deux flottants *)
maximum "ballon" "voiture";; (* deux chaines de caractères *)
- : int = 17
- : float = 1.06
- : string = "voiture"
maximum 1 1.0;; (* ERREUR : les arguments ne sont pas de même type *)
File "[11]", line 1, characters 10-13: 1 | maximum 1 1.0;; (* ERREUR : les arguments ne sont pas de même type *) ^^^ Error: This expression has type float but an expression was expected of type int
max;;
- : 'a -> 'a -> 'a = <fun>
Exercice 3 :¶
Définir globalement la fonction
fibo n
qui calcule le n-ieme terme de la suite de Fibonacci avec deux appels récursifsCalculer le 40e terme de la suite
NB : cette fonction est toujours de complexité exponentielle
let rec fibo n = if n < 2 then n else fibo (n-1) + fibo (n-2);;
fibo 40;;
val fibo : int -> int = <fun>
- : int = 102334155
- Définir
fibo n
comme une fonction locale pour calculerfibo 8
let rec fibo n = if n < 2 then n else fibo (n-1) + fibo (n-2) in fibo 8;;
- : int = 21
NB : les phrases OCaml deviennent de plus en plus longues
En OCaml, l'indentation n'est pas signifiante. On indente pour la lisiblité.¶
Comment indenter pour que le code soit le plus lisible possible ?
V. Conventions d'indentation¶
V.1 Convention : variable globale avec expression courte¶
let ... = ...;;
let random () = Random.float 1.0;;
val random : unit -> float = <fun>
V.2 Convention : variable globale avec expressions longues¶
let ... =
...................;;
let rec fibo n =
if n < 2 then n else fibo (n-1) + fibo (n-2);;
val fibo : int -> int = <fun>
V.3 Convention : variable locale avec expressions courtes¶
let ... = ... in expr;;
ou bien
let ... = ... in
expr;;
NB : dans let x = expr1 in expr2
, c'est expr2
qui est le résultat
let f x = x * x in f 4 + f 3 = f 5;;
- : bool = true
let f x = x * x in
f 4 + f 3 = f 5;;
- : bool = true
V.4 Convention : variable locale avec expressions longues¶
let ... =
...................
in
expr;;
let rec fibo n =
if n < 2 then n else fibo (n-1) + fibo (n-2)
in
fibo 8;;
- : int = 21
V.5 Convention : variables locales multiples¶
let .... = .... in
let .... = .... in
expr;;
Exercice 4 :¶
On souhaite réaliser l'expérience suivante. Choisir deux valeurs au hasard $x$ et $y$ de l'intervalle $[-1;1]$. Déterminer si le point $M(x,y)$ est dans le cercle de centre $O(0,0)$ et de rayon 1
Random.float x
renvoie unfloat
aléatoire de l'intervalle $[0,x]$Définir deux variables locales x et y
Quelle est la condition à vérifier ?
en respectant les conventions d'indentation
let x = Random.float 2.0 -. 1.0 in
let y = Random.float 2.0 -. 1.0 in
x**2.0 +. y**2.0 < 1.0;;
- : bool = true
Exercice 4 - suite :¶
- définir la fonction globale
experience ()
, qui réalise l'expérience précédente et renvoie un booléen
en respectant les conventions d'indentation
let experience () =
let x = Random.float 2.0 -. 1.0 in
let y = Random.float 2.0 -. 1.0 in
x**2.0 +. y**2.0 < 1.0;;
experience ();;
experience ();;
val experience : unit -> bool = <fun>
- : bool = true
- : bool = true
V.6 Convention : expression conditionnelle courte¶
if ... then ... else ...
Exercice 5 :¶
Ecrire la fonction globale
v_abs x
: valeur absolue d'un entier.Tester sur
-5
.
NB : cette fonction est prédéfinie en OCaml. C'est la fonction abs
let v_abs x = if x > 0 then x else -x;;
v_abs -5;; (* lu : (v_abs) - (5) *)
val v_abs : int -> int = <fun>
File "[2]", line 3, characters 0-5: 3 | v_abs -5;; (* lu : (v_abs) - (5) *) ^^^^^ Error: This expression has type int -> int but an expression was expected of type int
let v_abs x = if x > 0 then x else -x;;
v_abs (-5);; (* parentèses indispensables *)
val v_abs : int -> int = <fun>
- : int = 5
V.7 Convention : expression conditionnelle longue¶
if ... then
...
else
...
Exercice 6 :¶
Ecrire la fonction
maximum3 a b c
qui renvoie la plus grande valeur des 3 paramètresTester toutes les branches avec la fonction
assert : bool -> unit
qui lève une exception si la condition n'est pas vérifiée
let maximum3 a b c =
if a < b then
if b < c then c else b
else
if a < c then c else a;;
(* attention, il y a 4 cas *)
assert (maximum3 1 2 3 = 3);;
assert (maximum3 1 2 0 = 2);;
assert (maximum3 2 1 3 = 3);;
assert (maximum3 2 1 0 = 2);;
val maximum3 : 'a -> 'a -> 'a -> 'a = <fun>
- : unit = ()
- : unit = ()
- : unit = ()
- : unit = ()
V.8 Convention : expressions conditionnelles cas multiples¶
if ... then
...
else if ... then
...
else
...
Exercice 7 :¶
- Ecrire une fonction
signe x
qui renvoie"nul"
,"positif"
ou"négatif"
selon le signe de l'entierx
let signe x =
if x = 0 then
"nul"
else if x > 0 then
"positif"
else
"négatif";;
val signe : int -> string = <fun>
VI. Les fonctions anonymes¶
"où l'on voit qu'une fonction est une valeur comme une autre ..."
Définition d'une fonction anonyme¶
fun arg1 arg2 ... argn -> expr
: désigne une fonction anonyme (sans nom) ayant n paramètres
fun x y -> (3. *. x +. 7.) /. (4. -. y *. x);;
- : float -> float -> float = <fun>
on peut appliquer une fonction anonyme¶
(fun x y -> x + y) 4 5;;
- : int = 9
on peut définir une variable ayant pour valeur une telle fonction¶
let f = fun x y -> x + y;;
val f : int -> int -> int = <fun>
f 4 5;;
- : int = 9
NB : il y a équivalence entre les deux écritures
let f = fun x y -> x + y;;
let f x y = x + y;;
val f : int -> int -> int = <fun>
val f : int -> int -> int = <fun>
Ces deux ecritures désignent une fonction prenant deux paramètres et calculant la somme
on peut écrire une fonction renvoyant une autre fonction !!¶
Exercice 8¶
Ecrire une fonction cree_rand a b
qui renvoie une fonction qui renvoie un float
aléatoire de l'intervalle $[a,b]$
NB : Random.float x
renvoie un float
aléatoire de l'intervalle $[0,x]$
let cree_rand a b = (fun () -> a +. Random.float (b -. a));;
val cree_rand : float -> float -> unit -> float = <fun>
Lire val cree_rand : float -> float -> (unit -> float) = <fun>
La valeur renvoyée est une fonction de type (unit -> float)
let f_rand_0 = cree_rand (-1.) 1.;;
let f_rand_1 = cree_rand (-10.) 10.;;
f_rand_0 ();;
f_rand_0 ();;
f_rand_1 ();;
f_rand_1 ();;
val f_rand_0 : unit -> float = <fun>
val f_rand_1 : unit -> float = <fun>
- : float = 0.852077776721942293
- : float = -0.316722822803535808
- : float = 1.95645891706211827
- : float = 7.33763182029579
ATTENTION : les fonctions sont des valeurs comme les autres¶
une définition
let v = ...
peut être une définition de fonctionutiliser des noms de variables significatifs, des commentaires si nécessaire
VII. Les n-uplets (tuples)¶
(expr1, expr2, ..., exprn)
désigne un n-uplet
NB : les types des expressions composant le n-uplet ne sont pas forcément égaux
NB : type : T1 * T2 * ... * Tn
avec expri : Ti
NB : non modifiable
(2, 3);; (* un couple d'entiers *)
- : int * int = (2, 3)
(2, 'a');; (* un couple entier, caractère *)
- : int * char = (2, 'a')
Attention : les parenthèses ne sont pas indispensables
ce qui crée parfois des confusions ...
3,14;; (* Attention, les parenthèses ne sont pas indispensables *)
- : int * int = (3, 14)
NB : mettre systématiquement des parenthèses (dans un premier temps)
(2, 3.14, "coucou", fun x -> 2 * x);; (* un quadruplet de ... *)
- : int * float * string * (int -> int) = (2, 3.14, "coucou", <fun>)
NB : n-uplet pour $n$ supérieur ou égal à 2
1-uplet ? : c'est le parenthésage habituel
0-uplet ? : c'est la valeur
()
("hello");;
();;
- : string = "hello"
- : unit = ()
On peut définir un couple de variables¶
let (x, y) = expr
:expr
doit être de type coupleNB : un peu comme en Python. Permet de récupérer directement les deux composantes d'un couple
On peut définir un argument sous forme de couple¶
let f (x, y) = ...
: la fonctionf
prend en argument un couple dont la première composante est appeléex
et la deuxièmey
NB : ne pas confondre avec la notation C / Python de fonction à deux paramètres
NB : se généralise aux n-uplets
Exercice 9¶
Ecrire une fonction
pointAlea x
qui renvoie un couple de coordonnées aléatoires de $[0,x]²$Ecrire une fonction
distance_origine
qui prend un couple de coordonnées et renvoie sa distance à l'origine
let pointAlea x =
let px = Random.float x in
let py = Random.float x in
(px, py);;
let p1 = pointAlea 10.;; (* une variable de type couple de float *)
let (px, py) = pointAlea 10.;; (* un couple de variables de type float *)
val pointAlea : float -> float * float = <fun>
val p1 : float * float = (3.9650094694195066, 1.90217751234253374)
val px : float = 5.67660068680959107 val py : float = 4.035922677801147
let distance_origine (x, y) = sqrt (x**2. +. y**2.);;
distance_origine p1;;
distance_origine (px, py);;
val distance_origine : float * float -> float = <fun>
- : float = 4.39767886288300769
- : float = 6.96508917521353066
Exercice 10¶
Ecrire la fonction
premiere_composante
qui prend un couple en argument et renvoie sa première composante.Quel est le type de cette fonction ?
NB c'est la fonction fst
en OCaml (snd
pour la seconde composante)
let premiere_composante (x, y) = x;; (* fonction polymorphe *)
premiere_composante (7, "toto");;
val premiere_composante : 'a * 'b -> 'a = <fun>
- : int = 7
fst;;
snd;;
- : 'a * 'b -> 'a = <fun>
- : 'a * 'b -> 'b = <fun>
VIII. Fonctions "Curryfiées"¶
Quelle(s) différence(s) entre les deux fonctions suivantes :
let sumC x y = x + y;;
let sumD (x, y) = x + y;;
Leurs types !
let sumC x y = x + y;; (* fonction curryfiée *)
let sumD (x, y) = x + y;; (* fonction décurryfiée *)
val sumC : int -> int -> int = <fun>
val sumD : int * int -> int = <fun>
let sum x y = x + y;;
: fonction "curryfiée" ayant deux argumentslet sum (x, y) = x + y;;
: fonction "décurryfiée" ayant un seul argument de type couple
NB : de Haskell Curry, mathématicien, logicien qui a posé les bases des langages fonctionnels
Application partielle¶
En OCaml, lorsqu'une fonction a 2 arguments, il est possible de ne passer que le premier
Avantage à la fonction curryfiée :
- on peut lui passer seulement le premier argument : application partielle
- on peut lui passer les deux arguments : application (totale)
NB : la fonction décurryfiée n'a qu'un seul argument :
- on ne peut lui passer que
x
ety
simultanément
let sumC x y = x + y;; (* fonction curryfiée *)
let f = sumC 3;; (* qu'est ce que f ??? *)
val sumC : int -> int -> int = <fun>
val f : int -> int = <fun>
f 4;; (* f est la fonction qui ajoute 3*)
f 5;;
- : int = 7
- : int = 8
NB : pour une fonction à $n$ arguments, on peut passer - uniquement le premier - uniquement les deux premiers - ...
Par exemple, pour f:'a->'b->'c->'d
:
f x
(avecx:a
) est de typeb->'c->'d
: application partiellef x y
(avecx:a
ety:b
) est de typec->d
: application partiellef x y z
(avecx:a
,y:b
etz:c
) est de typed
: application (totale)