Programmation fonctionnelle

publicité
Programmation fonctionnelle
Notes de cours
Cours 1
12 Septembre 2012
Sylvain Conchon
[email protected]
1/32
Calendrier
Les TD commencent la semaine du 17 Septembre
Au total : 10 cours de 1h45, 11 TD de 3h
Contrôle des connaissances :
I
partiel : semaine du 22 Octobre (à confirmer), coef. 0, 4
I
examen : semaine du 10 Décembre (à confirmer), coef. 0, 6
Retrouver toutes ces informations (et d’autres) sur le web
http://www.lri.fr/~conchon/PF/
2/32
Objectifs de ce cours
Programmer, programmer, programmer. . .
I
acquérir de bons principes de programmation (indépendants
du langage de programmation)
I
programmer les algorithmes vus en cours d’algorithmique
I
savoir exploiter au mieux les atouts de la programmation
fonctionnelle dans vos applications futures
3/32
Le langage Ocaml
http://caml.inria.fr
I
I
langage développé à l’INRIA (Institut National de Recherche
en Informatique et en Automatique)
disponible sur de nombreuses architectures (Linux, Windows,
Mac OS X etc.)
Références :
I
I
I
E. Chailloux, P. Manoury, B. Pagano
Développement d’Applications avec Objective Caml
http://www.pps.jussieu.fr/Livres/ora/DA-OCAML/
P. Weis, X. Leroy
Le Langage Caml
http://caml.inria.fr/pub/distrib/books/llc.pdf
G. Cousineau, M. Mauny
Approche Fonctionnelle de la Programmation
http://pauillac.inria.fr/cousineau-mauny/
4/32
Principe du déroulement de ce cours
1 cours
=
1 programme
Notes de cours :
I
un poly d’introduction à OCaml ;
I
des transparents qui contiennent les nouvelles notions du
langage Ocaml nécessaires pour réaliser le programme étudié ;
I
un listing du code du programme étudié
5/32
Le programme de cette semaine
La fractale de Mandelbrot
6/32
L’ensemble de Mandelbrot
Cet ensemble est défini comme l’ensemble des points (a, b) du plan
pour lesquels la suite récurrente suivante ne diverge pas vers l’infini.
x0
y0
xn+1
yn+1
=
=
=
=
0
0
xn2 − yn2 + a
2xn yn + b
I
on peut démontrer que la suite diverge dès que xn2 + yn2 > 4
I
le calcul ne s’arrête pas pour les points appartenant à
l’ensemble ; il faut donc fixer le nombre maximal d’itérations
devant être fait par le programme
On dessinera cet ensemble dans une fenêtre de taille 800 × 800
pour des valeurs de (a, b) dans [−2, 1] × [− 23 , 32 ]. Pour faire joli, on
peut afficher les points avec une couleur qui dépend du nombre
d’itérations nécessaire pour s’arrêter.
7/32
Les notions abordées avec ce programme
I
la forme des programmes Ocaml
I
la boucle de compilation
I
la boucle d’interaction
I
les types et expressions élémentaires
I
les séquences d’instructions
I
les n-uplets
I
les déclarations de constantes
I
les déclarations de fonctions et fonctions récursives
I
l’inférence de type et les erreurs de typage
I
les boucles for
I
la bibliothèque graphique Graphics
8/32
La forme des programmes Ocaml
Il s’agit simplement d’une suite de déclarations de la forme :
let motif1 . . . motifn = expression
I
ces déclarations sont évaluées de haut en bas
I
les motifs peuvent être des identificateurs ou des valeurs
comme () (nous verrons cela par la suite quand nous
étudierons le filtrage)
I
l’évaluation d’une déclaration consiste à évaluer l’expression à
droite du symbole =, puis à associer le résultat obtenu au
motif motif1
Il n’y a donc pas de point d’entrée particulier (fonction
principale par ex.) comme dans d’autres langages.
9/32
La boucle de compilation
I
fichier hello.ml
let () = Printf.printf "Hello world!\n"
I
compilation
> ocamlc -o hello hello.ml
I
exécution
> ./hello
Hello world!
>
Remarque : le caractère > est le prompt du terminal
10/32
Options du compilateur
ocamlc <options> <fichiers>
Options courantes :
I
I
I
I
I
I
-o <fichier> fixe le nom de l’exécutable à <fichier>
-c compile sans édition de liens (pour fabriquer des modules
séparemment)
-a construit une bibliothèque
-dtypes sauvegarde des informations de typage (visibles dans
emacs)
-I <dir> ajoute <dir> à la liste des répertoires de
bibliothèques et/ou modules
-g ajoute des informations de débuggage accessibles à
runtime via ocamlrun
N’hésitez pas à taper ocamlc -help ou man ocamlc dans
votre terminal pour avoir la liste complète des options.
11/32
Bytecode ou code natif
Il existe deux versions du compilateur Ocaml.
I
ocamlc produit des fichiers (modules ou exécutables) en
bytecode
I
ocamlopt génère du code machine directement exécutable
par l’ordinateur
Avantages et inconvénients de ces formats :
I
les fichiers en bytecode ont l’avantage de pouvoir être
exécutés sur n’importe quelle plate-forme, sans être
re-compilés (c’est le format choisi par exemple en Java)
I
un fichier en langage machine s’exécute plus rapidement
12/32
La boucle d’interaction
Ocaml dispose d’un interpréteur (toplevel), très utile pour tester
des petits programmes.
I
exécution du toplevel
> ocaml
Objective Caml version 3.11.1
#
I
interaction
# let () = Printf.printf "Hello world!\n";;
Hello world!
- : unit = ()
Remarque : le caractère # est le prompt du toplevel
13/32
Types et expressions élémentaires
Expressions de type int
#
#
#
#
-
4 + 1 - 2 * 2 ;;
: int = 1
5 / 2 ;;
: int = 2
1 000 005 mod 2 ;;
: int = 1
max int + 1 ;;
: int = -1073741824
I
int représente les entiers compris entre −230 et 230 − 1 (sur
une machine 32 bits)
I
opérations sur ce type : +, -, *, / (division entière), mod (reste
de la division) etc.
14/32
Types et expressions élémentaires
Expressions de type float
#
#
#
#
-
4.3e4 +. 1.2 *. -2.3 ;;
: float = 42997.24
5. /. 2. ;;
: float = 2.5
1. /. 0. ;;
: float = infinity
0. /. 0. ;;
: float = nan
I
float représente les nombres flottants à l’aide d’une mantisse
m et d’un exposant n (pour coder le nombre m × 10n )
I
les types int et float sont disjoints
I
opérations sur ce type : +. -. *. /. sqrt cos etc.
I
on passe d’un entier à un flottant à l’aide de la fonction
float et inversement avec truncate
15/32
Types et expressions élémentaires
Expressions de type bool
#
#
#
#
I
I
I
I
false || true ;;
: bool = true
3 <= 1 ;;
: bool = false
not (0=2) && 1>=3 ;;
: bool = false
if 2<0 then 2.0 else (4.6 *. 1.2) ;;
: float = 5.52
bool représente les valeurs true (vrai) et false (faux)
opérations sur ce type not (non), && (et) et || (ou)
les opérateurs de comparaison (=, <, >, <=, >=) retournent des
valeurs booléennes
dans une conditionnelle de la forme
if exp1 then exp2 else exp3
l’expression exp1 doit être de type bool.
16/32
Types et expressions élémentaires
Expressions de type char
#
#
#
-
’a’ ;;
: char = ’a’
int of char ’a’ ;;
: int = 97
char of int 100 ;;
: char = ’d’
I
char représente les caractères
I
ces valeurs sont encadrées de deux apostrophes ’
I
la fonction int of char renvoie le code ASCII d’un caractère
(et inversement pour la fonction char of int).
17/32
Types et expressions élémentaires
Expressions de type string
I
I
I
I
I
# "hello" ;;
- : string = "hello"
# "" ;;
- : string = ""
# "bon" ^ "jour" ;;
- : string = "bonjour"
# "hello".[1] ;;
- : char = ’e’ # string of int 123 ;;
- : string = "123"
string représente les chaı̂nes de caractères
ces valeurs sont encadrées par deux guillemets "
l’opérateur ^ permet de concaténer des chaı̂nes
l’opération .[i] permet d’accéder au ie caractère d’une chaı̂ne
(le premier caractère est à l’indice 0)
des fonctions de conversions permettent de convertir des
valeurs de types de base en chaı̂nes (et inversement)
18/32
Types et expressions élémentaires
Expressions de type unit
# () ;;
- : unit = ()
# Print.printf "bonjour\n" ;;
bonjour
- : unit = ()
I
unit représente les expressions sans réelle valeur (ex.
fonctions d’affichage)
I
une seule valeur a ce type, elle est notée ()
I
c’est l’équivalent du type void en C
19/32
Séquence d’instructions
# Print.printf "bonjour\n"; 5 ;;
bonjour
- : int = 5
I
le point virgule ; permet d’évaluer une séquence d’expressions
Les expressions de type unit sont identiques à des instructions
Étant donnée une séquence d’expressions e1 ;e2 ;· · · ;en
I
toutes les expressions e1 , . . . , en−1 doivent être de type unit
I
la valeur et le type de la séquence sont ceux de la dernière
expression en
20/32
Les n-uplets
Expressions de type produit
#
#
#
#
#
-
(1, ’a’) ;;
: int * char = (1, ’a’)
(1 + 2, ’a’, true && false) ;;
: int * char * bool = (3, ’a’, false)
("hello", (1, 3.4)) ;;
: string * (int * float) = ("hello", (1, 3.4))
fst (1, ’a’) ;;
: int = 1
snd ((1, ’a’), (2+3, true, "a")) ;;
: int * bool * string = (5, true, "a")
I
le type produit * représente les n-uplets
I
les n-uplets peuvent être arbitrairement imbriqués
I
fst et snd permettent d’accéder respectivement à la première
et à la deuxième composante d’une valeur de type τ1 * τ2
21/32
Les n-uplets
Attention à ne pas confondre les types des expressions suivantes :
#
#
#
I
(1, 2, 3) ;;
: int * int * int = (1, 2, 3)
((1, 2), 3) ;;
: (int * int) * int = ((1, 2), 3)
(1, (2, 3)) ;;
: int * (int * int) = (1, (2, 3))
int * int * int désigne un triplet d’entiers
I
(int * int) * int est une paire dont la première
composante est une paire d’entiers et la deuxième un entier
I
int * (int * int) est une paire dont la première
composante est un entier et la deuxième une paire d’entiers
22/32
Constantes globales
# let x = 3 ;;
val x : int = 3
I
I
I
I
le type est inféré automatiquement par le compilateur
le contenu d’une constante n’est pas modifiable
la portée est limitée aux déclarations suivantes
# let y = 5 + x ;;
val y : int = 8
# let z = 10 + z ;;
Error: Unbound value z
La liaison est statique : la redéfinition ne change pas la valeur
des expressions précédentes
# let x = 10 ;;
val x : int = 10
# y ;;
- : int = 8
23/32
Constantes locales
# let x = 3 in x + 1;;
- : int = 4
I
I
la portée est limitée à l’expression qui suit le in
# x + 2;;
Error: Unbound value x
le nom de la constante locale masque toute déclaration
antérieure de même nom
# let y = 2;;
val y : int = 2
# let y = 100 in y + 1;;
- : int = 101
# y + 3;;
- : int = 5
24/32
Fonctions
# let f x = x + 2;;
val f : int -> int = <fun>
# f 4;;
- : int = 6
I
les types, des arguments et du résultat, sont inférés
I
la règle de portée du nom de la fonction est identique à celle
des constantes (globales ou locales)
# let h
- : int
# h 4;;
Error :
# let g
Error :
x = x / 2 in h 6;;
= 3
Unbound value h
x = x || g (not x);;
Unbound value g
25/32
Fonctions à
plusieurs arguments # let f x y z =
if x then y + 1 else z - 1;;
val f : bool -> int -> int -> int = <fun>
# f true 2 3;;
- : int = 3
I
les paramètres ne sont pas entre parenthèses, ni dans les
déclarations, ni dans les applications de fonctions
Attention : La fonction suivante prend en fait un seul paramètre
# let f (x, y, z) =
if x then y + 1 else z - 1;;
val f : bool * int * int -> int = <fun>
# f (true, 2, 3);;
- : int = 3
26/32
Opérateurs
# ( + );;
- : int -> int -> int = <fun>
# let ( ++ ) (x1, y1) (x2, y2) = (x1 + x2, y1 + y2);;
val ++: int = 3
I
un opérateur est simplement une fonction infixe
I
on manipule cette fonction à l’aide de la notation ( op )
I
les noms des opérateurs ne doivent contenir que des symboles
+, *, $ etc.
27/32
Fonctions récursives
# let rec fact x =
if x <= 0 then 1
else x * fact (x - 1);;
val fact : int -> int = <fun>
# fact 4;;
- : int = 24
I
l’ajout du mot-clé rec change la portée de l’identificateur : il
est alors accessible dans la définition de la fonction
28/32
La boucle for
# for i = 1 to 5 do
Printf.printf "%d " i
done;;
- : unit = ()
1 2 3 4 5
I
I
I
I
i est non modifiable et visible que dans le corps de la boucle
il est incrémenté automatiquement à chaque tour de boucle (il
peut être décrémenté en utilisant downto à la place de to)
l’expression entre do et done doit être de type unit
les expressions des bornes ne sont évaluées qu’une seule fois
# let f i = Printf.printf "%d " i; i
# for i = f 1 to f 5 do
Printf.printf "."
done;;
- : unit = ()
1 5 .....
29/32
L’inférence de type et les erreurs de typage
# let x = 3 + "hello";;
Error: This expression has type string but an
expression was expected of type int
# let f x = x - 1;;
val f : int -> int = <fun>
# f 3 2;;
Error: This function is applied to too many arguments
# for i = 0 to 5 do i+1 done;;
Warning: This expression should have type unit
I
bien indenter les programmes (utiliser un mode d’indentation
automatique)
I
lire les messages d’erreurs
30/32
La bibliothèque Graphics
I
ajouter le fichier graphics.cmxa dans la commande de
compilation (ocamlopt ... graphics.cmxa ...)
I
la directive open Graphics permet d’accéder directement
aux fonctions de la bibliothèque Graphics
I
open graph " 400x300" ouvre une fenêtre graphique de 400
pixels de large et 300 de haut ; le pixel avec les coordonnées
(0, 0) est en bas à gauche
I
plot x y affiche un pixel en (x, y ) dans la couleur courante
I
rgb r v b renvoie une couleur calculée à partir de
composantes rouge, vert et bleu (entiers entre 0 et 255) ; les
couleurs prédéfinies : white, black, blue, green etc.
I
set color c fixe la couleur courante à la valeur c
I
let st = wait next event [Button down] attend un clic
de souris ; les coordonnées sont st.mouse x et st.mouse y
31/32
Exercice à faire à la maison
I
modifier le programme mandelbrot.ml afin de pouvoir
zoomer sur une région particulière de la fractale ; laisser
l’utilisateur déterminer cette région à l’aide de la souris
I
afficher les points avec une couleur qui dépend du nombre
d’itérations nécessaire pour s’arrêter
32/32
Téléchargement