Institut Galil´ee AIR3 S1– Ann´ee 2013–2014
Programmation fonctionnelle (Ocaml)
Notes de cours : Bases
1 Documentation
Beaucoup d’informations, notamment le manuel et des livres en acc`es libre, sont disponible `a partir de
http://caml.inria.fr. Regarder en particulier http://caml.inria.fr/pub/docs/manual-ocaml-4.
01/libref/Pervasives.html pour la liste des fonctions d´efinies et utilisables de base.
2 Introduction
Concepts cl´es de la programmation fonctionnelle (et de Caml). Ces concepts se retrouvent dans
d’autres langages de programmation, y compris des langages non-fonctionnels qui incluent des aspects
fonctionnels (exemple : fonctions anonymes en Java ou JavaScript).
Ordre sup´erieur (les fonctions sont des “citoyens de premi`ere classe”).
Typage fort (notamment, diff´erence int/float). Garanti l’ex´ecution sans erreur (il peut y avoir
des exceptions).
Inf´erence de type (pas besoin de donner le type des variables).
Listes et r´ecurrence.
Style applicatif : on ne modifie pas un ´etat de la m´emoire, on calcule des nouvelles valeurs.
Inspir´e du λ-calcul (Church).
3 Une grosse calculette
Quelques phrases Caml et leurs r´esultats comment´es.
# 1;;
Le #est le prompt de l’interpr`ete. Une phrase Caml se termine par ;; qui dit `a l’interpr`ete d’´evaluer ce
qu’on a tap´e.
-:int=1
Le type du r´esultat est d´etermin´e automatiquement par Caml (inf´erence de type).
# 1.5;;
- : float = 1.5
# ’a’;;
- : char = ’a’
# "azerty";;
- : string = "azerty"
# true;;
- : bool = true
Quelques types de base.
La r´eponse de l’interpr`ete est de la forme <nom> : <type> = <valeur>. Ici, rien, n’est nomm´e.
# 1+3;;
-:int=4
Une grosse calculatrice. . .
1
# 1+3.9;;
Error: This expression has type float but an expression was expected of type int
# 1.;;
- : float = 1.
# 1.+3.9;;
Error: This expression has type float but an expression was expected of type int
# 1. +. 3.9;;
- : float = 4.9
Le typage est tr`es strict. int et float sont deux types diff´erents qui ne peuvent pas ˆetre m´elang´es (pas
de cast automatique). Les op´erations usuelles sur les float sont suivies d’un point (+.,*.,/., . . . )
Noter la fa¸con dont Caml indique les erreurs de type, c’est l’une de celles qu’on voit le plus souvent. . .
L’erreur de compilation This expression has type toto but an expression was
expected of type titi signifie en g´en´eral :
Si toto/titi sont int/float, qu’on a utilis´e +au lieu de +. (ou variations).
Si toto et titi sont diff´erents (surtout si l’un des deux est une fonction ou un n-uplet),
qu’on a mal parenth´es´e l’expression et que Caml n’a pas les priorit´es qu’on croit.
Si toto et titi sont le mˆeme type (d´efini par l’utilisateur) : Achievement unlocked. . .
# 1<4;;
- : bool = true
# float_of_int 4;;
- : float = 4.
# float_of_int 4.;;
Error: This expression has type float but an expression was expected of type int
Des op´erations dont le type du r´esultat n’est pas celui des arguments.
# float_of_int;;
- : int -> float = <fun>
Les fonctions sont des “citoyens de premi`ere classe”. Une fonction peut donc ˆetre “´evalu´ee” toute seule,
sans arguments. Le type d’une fonction comporte une fl`eche (->) ainsi que les types de d´epart et d’arriv´ee
(comme la notation math´ematique f:AB). La valeur d’une fonction est toujours la valeur abstraite
<fun>.
4 Tests et bool´eens
# if 2<4 then 4. else 5.;;
- : float = 4.
# if 2<4 then 4. else 5;;
Error: This expression has type int but an expression was expected of type float
# if 2<4 then 4 else 5;;
-:int=4
# float_of_int (if 2<4 then 4 else 5);;
- : float = 4.
On peut faire des tests. Le type de chaque branche doit ˆetre le mˆeme, car c’est le type du r´esultat (on
peut le passer en argument `a une autre fonction). Le if ... then ... else ... est donc similaire au
... ? ... : ... de C. Par exemple, on peut ´ecrire en C 2<4 ? 4 : 5.
# true && true;;
- : bool = true
# true || false;;
- : bool = true
# not true;;
- : bool = false
Les op´erations bool´eennes de base.
2
5 Nommer des objets
# let x=4;;
val x : int = 4
# x;;
-:int=4
# let x=2;;
val x : int = 2
# x;;
-:int=2
On peut nommer une valeur avec la construction let ... =, on peut r´eutiliser ce nom par la suite. Si on
redonne le mˆeme nom `a une autre valeur, on d´etruit la premi`ere (c’est-`a-dire qu’on cr´ee une deuxi`eme
variable de mˆeme nom, on ne modifie pas le contenu de la premi`ere).
# let f x = x+1;;
val f : int -> int = <fun>
# let f = function x -> 2*x;;
val f : int -> int = <fun>
Deux mani`eres de d´efinir des fonctions. On peut mettre les arguments `a gauche du =ou on peut introduire
un argument `a l’aide du mot cl´e function.
Noter le type des fonctions qui comporte une fl`eche. L’utilisation de function est proche de la
notation math´ematique
fest la fonction x7→ 2×x.
# function x -> 2*x;;
- : int -> int = <fun>
# (function x -> x+2) 4;;
-:int=6
On n’a pas besoin de nommer une fonction pour la d´efinir. La fonction est une valeur comme une autre.
On parle alors de “fonction anonyme”. Bien ´evidemment, on ne peut utiliser une fonction anonyme que
imm´ediatement apr`es sa d´efinition puisque sa valeur est ensuite d´etruite.
6 Bizarreries syntaxiques
#letgx=
x+3 -12;;
val g : int -> int = <fun>
Une phrase Caml peut ˆetre sur plusieurs lignes. Seul le ;; termine la phrase.
# (1+2)*3;;
-:int=9
# begin 1+2 end *3;;
-:int=9
les parenth`eses et le begin ...end sont strictement ´equivalent. L’usage est d’utiliser des parenth`eses
dans une expression et des begin ...end pour grouper plusieurs expressions.
# let f x = x+1
let g y = 2*y;;
val f : int -> int = <fun>
val g : int -> int = <fun>
D´efinitions simultan´ees de fonctions. Caml d´ecouvre tout seul o`u se termine la d´efinition de fet les ;;
ne sont donc pas n´ecessaires. En particulier, dans un fichier de code isol´e (et inclus dans l’interpr`ete ou
compil´e), on ne met g´en´eralement pas les ;;
3
7 R´ecurrence
# let rec fact n = if n=0 then 1 else n*(fact (n-1));;
val fact : int -> int = <fun>
# let fac n = if n=0 then 1 else n*(fac (n-1));;
Error: Unbound value fac
Une fonction r´ecursive se d´efinit avec un let rec au lieu du simple let.
L’erreur de compilation Unbound value toto signifie g´en´eralement :
Soit qu’on a mis un let au lieu d’un let rec.
Soit qu’on a fait une faute de frappe sur le nom d’une variable ou d’une fonction.
8 Couples, n-uplets
# 1,4;;
- : int * int = (1, 4)
# 1,"e",4.;;
- : int * string * float = (1, "e", 4.)
# let f (x,y) = x+y;;
val f : int * int -> int = <fun>
Un n-uplet regroupe plusieurs valeurs s´epar´ees par des virgules. Les valeurs peuvent ˆetre de types
diff´erents. Le type du n-uplet est not´e avec des *. Similaire au produit cart´esien en math´ematiques,
not´e A×B.
# f 1,6;;
Error: This expression has type int but an expression was expected of type int * int
# f (1,6);;
-:int=7
Attention au parenth´esage implicite (priorit´e des op´erations). L’application a une priorit´e plus ´elev´ee
que le couple. f 1,6 est donc interpr´et´e comme (f 1),6. Noter l’erreur “mauvais type” dans ces cas de
parenth`eses oubli´ees.
# let add x y = x+y;;
val add : int -> int -> int = <fun>
Une fonction `a deux arguments xet ypeut ˆetre vue comme une fonction avec un seul argument, x, qui
renvoie une fonction (qui attend yet donne le r´esultat). Le passage d’une fonction sur les couples vers
cette notation s’appelle “curryfication”.
Math´ematiquement, il y a un isomorphisme entre A×BC(ensemble des fonctions `a 2 arguments,
repr´esent´es par un produit cart´esien) et A(BC) (ensemble des fonctions de Avers “les fonctions
de Bvers C”).
Noter que le type devrait ˆetre int -> (int -> int). L’application (et la fl`eche) est associative `a
droite, donc on peut se passer des parenth`eses.
En pratique, une fonction dont le type comporte plusieurs fl`eches est une fonction `a plusieurs
arguments.
# let add5 = add 5;;
val add5 : int -> int = <fun>
# add5 7;;
- : int = 12
La curryfication permet l’application partielle. Si on ne fournit qu’un argument `a la fonction, on cr´ee
une nouvelle fonction.
L’application partielle permet un style de programmation compact et lisible. Aussi, on curryfie
(presque) toujours les fonctions `a plusieurs arguments et il est tr`es rare d’avoir une fonction qui prend
un couple comme argument.
4
9 Ordre sup´erieur
# let carre_couple (x,y) = (x*x, y*y);;
val carre_couple : int * int -> int * int = <fun>
# let double_couple (x,y) = (2*x, 2*y);;
val double_couple : int * int -> int * int = <fun>
# let app_couple f (x,y) = (f x, f y);;
val app_couple : (’a -> ’b) -> ’a * ’a -> ’b * ’b = <fun>
Let fonctions carre_couple et double_couple ont la mˆeme structure : appliquer une fonction aux
membres d’un couple. Grˆace `a l’ordre sup´erieur, on peut d´efinir une fonction plus g´en´erique qui attend
la fonction `a appliquer.
Noter dans le type de app_couple :
les ’a et ’b. On ne sait pas ici quel sera le type des ´el´ements du couple. La fonction marche quelque
soit le type ’a et le type ’b. Mais il faut bien que les deux ´el´ements du couple aient le mˆeme type
puisqu’ils sont tous les deux arguments de f.
On parle de polymorphisme.’a et ’b sont appel´ees des variables de type. On les lit parfois α, β en
r´ef´erence au λ-calcul.
les parenth`eses. ’a -> ’b -> ’a * ’a -> ’b * ’b serait une fonction `a 3 arguments, respective-
ment de type ’a,’b et ’a * ’a. Autrement dit, l’application (et la fl`eche) n’est pas associative `a
gauche.
# let carre_double = app_couple (function x -> x*x);;
val carre_double : int * int -> int * int = <fun>
# let doublef_couple = app_couple (function x -> 2. *. x);;
val doublef_couple : float * float -> float * float = <fun>
# let convert_couple = app_couple (float_of_int);;
val convert_couple : int * int -> float * float = <fun>
L’application partielle permet de d´efinir beaucoup de fonctions agissant sur les couples.
# let compose f g = function x -> f( g(x) );;
val compose : (’a -> ’b) -> (’c -> ’a) -> ’c -> ’b = <fun>
Le grand classique de l’ordre sup´erieur. . .
10 Reconnaissance de motif (“Pattern matching”)
# let rec fac =
function
|0->1
| n -> fac (n-1) * n;;
val fac : int -> int = <fun>
# let rec fact n = if n=0 then 1 else n*(fact (n-1));;
val fact : int -> int = <fun>
La construction function peut aussi introduire une reconnaissance de motif. Il s’agit d’une g´en´eralisation
du switch case. En particulier, les valeurs reconnues ne sont pas forc´ement constantes et peuvent d´efinir
des nouvelles variables (ici, n).
# let rec fibo =
function
|0->1
|1->1
| n -> (fibo (n-1)) + (fibo (n-2));;
val fibo : int -> int = <fun>
5
1 / 8 100%
La catégorie de ce document est-elle correcte?
Merci pour votre participation!

Faire une suggestion

Avez-vous trouvé des erreurs dans linterface ou les textes ? Ou savez-vous comment améliorer linterface utilisateur de StudyLib ? Nhésitez pas à envoyer vos suggestions. Cest très important pour nous !