Docsity
Docsity

Prépare tes examens
Prépare tes examens

Étudies grâce aux nombreuses ressources disponibles sur Docsity


Obtiens des points à télécharger
Obtiens des points à télécharger

Gagnz des points en aidant d'autres étudiants ou achete-les avec un plan Premium


Guides et conseils
Guides et conseils

Notes sur l'initiation à la programmation fonctionnelle - 1° partie, Notes de Introduction à la programmation informatique

Notes d’informatique sur l'initiation à la programmation fonctionnelle - 1° partie. Les principaux thèmes abordés sont les suivants: Fondamentaux, Le premier programme, La boucle d'interaction ocaml, Allocation mémoire.

Typologie: Notes

2013/2014

Téléchargé le 06/03/2014

Gabrielle89
Gabrielle89 🇫🇷

4.4

(75)

499 documents

1 / 15

Documents connexés


Aperçu partiel du texte

Télécharge Notes sur l'initiation à la programmation fonctionnelle - 1° partie et plus Notes au format PDF de Introduction à la programmation informatique sur Docsity uniquement! Université Paris Sud Master Informatique M1 20052006 Initiation à la programmation fonctionnelle Jean-Christophe Filliâtre Table des matières 1 Fondamentaux 5 1.1 Premiers pas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.1.1 Le premier programme . . . . . . . . . . . . . . . . . . . . . . . . 5 1.1.2 Déclarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.1.3 Références . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.1.4 Expressions et instructions . . . . . . . . . . . . . . . . . . . . . . 8 1.1.5 Variables locales . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.2 La boucle d'interaction ocaml . . . . . . . . . . . . . . . . . . . . . . . . 10 1.3 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.3.1 Syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.3.2 Fonctions comme valeurs de première classe . . . . . . . . . . . . 13 1.3.3 Fonctions récursives . . . . . . . . . . . . . . . . . . . . . . . . . . 17 1.3.4 Polymorphisme . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.4 Allocation mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.4.1 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 1.4.2 Enregistrements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 1.4.3 N-uplets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.4.4 Listes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 1.4.5 Types construits . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 1.5 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2 Modules 28 2.1 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.1.1 Fichiers et modules . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.1.2 Encapsulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.1.3 Langage de modules . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.2 Foncteurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3 Persistance 37 3.1 Structures de données immuables . . . . . . . . . . . . . . . . . . . . . . 37 3.2 Intérêts pratiques de la persistance . . . . . . . . . . . . . . . . . . . . . 39 3.3 Interface et persistance . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 2 Chapitre 1 Fondamentaux Le terme de programmation fonctionnelle désigne une famille de langages de program- mation ayant un certain nombre de traits en commun, dont un rôle central donné aux fonctions, d'où le nom. Les représentants les plus connus de cette famille se nomment Haskell, Standard ML et Ocaml, parmi de nombreux autres. La programmation fonctionnelle est très souvent opposée à la programmation impé- rative, famille regroupant des langages tels que C ou Pascal mais aussi Java ou C++, où l'on procède essentiellement par eets de bord i.e. modication en place du contenu des variables. Mais cette opposition n'a pas vraiment lieu d'être. En eet, seul le langage Haskell exclut complètement les eets de bord et est dit pour cela purement fonctionnel. Les langages tels que Standard ML ou Ocaml autorise parfaitement les eets de bords et comportent d'ailleurs toutes les structures usuelles de la programmation impérative (variables modiables en place, tableaux, boucles for et while, entrées-sorties, etc.) Malheureusement, comme nous le verrons à plusieurs reprises, le terme fonctionnel évoque souvent l'absence d'eets de bord ou le caractère immuable, comme dans l'expres- sion malheureuse  structure de données (purement) fonctionnelle . Mais en réalité, les langages de programmation fonctionnels partagent beaucoup de points avec ceux dits im- pératifs. Et ils ont également beaucoup de points forts en dehors du caractère immuable de certaines de leurs valeurs ou de leur gestion des fonctions. C'est ce que nous allons essayer d'illustrer dans ce cours. Dans ce qui suit, il nous arrivera parfois de faire un parallèle avec un morceau de code C ou Java. Que le lecteur ne connaissant pas l'un ou l'autre ne s'inquiète pas et se contente de l'ignorer. 1.1 Premiers pas 1.1.1 Le premier programme Le programme le plus célèbre, parce que le  premier , est celui qui se contente d'acher hello world ! sur la sortie standard. En Ocaml il s'écrit ainsi print_string "hello world!\n" Si le texte ci-dessus est contenu dans un chier hello.ml, il est compilé en un exécutable hello avec la commande 5 % ocamlc -o hello hello.ml exactement comme avec un compilateur C, puis cet exécutable peut être lancé, donnant le résultat escompté : % ./hello hello world! % On constate tout de suite deux diérences importantes avec le même programme écrit en C ou en Java. D'une part, l'application d'une fonction s'écrit en Ocaml par simple juxtaposition de la fonction et de son argument. À la diérence de la plupart des langages où l'application de f à x doit s'écrire f(x), on se contente ici d'écrire f x. Comme dans les autres langages, les parenthèses peuvent  et doivent  être utilisées lorsque les priorités des opérateurs l'exigent, comme dans l'expression 2*(1+3). Rien n'interdit donc en particulier d'écrire print_string("hello world!\n") mais les parenthèses autour de la chaîne de caractères sont tout simplement inutiles. On constate d'autre part qu'il n'est nul besoin de dénir une fonction principale main contenant le code à exécuter. Le programme Ocaml est constitué ici d'une simple expression à évaluer. Cette expression est l'application d'une fonction, print_string, à un argument, la chaîne de caractères "hello world!\n". On aurait très bien pu écrire un programme se réduisant à 1+2 qui aurait eu pour eet de calculer le résultat de 1+2. Mais rien n'aurait été aché. Pour cela, il faudrait écrire par exemple le programme print_int (1+2). 1.1.2 Déclarations Plus généralement, un programme Ocaml est constitué d'une suite quelconque d'ex- pressions à évaluer et de déclarations, séparées par un double point-virgule ;;. Une décla- ration aecte le résultat de l'évaluation d'une expression à une variable, et est introduite par le mot clé let. Ainsi le programme suivant let x = 1 + 2;; print_int x;; let y = x * x;; print_int y;; calcule le résultat de 1+2, l'aecte à la variable x, ache la valeur de x, puis calcule le carré de x, l'aecte à la variable y et enn ache la valeur de y. Une déclaration telle que let x = 1+2 peut être vue comme l'introduction d'une variable globale x. Mais il y a là beaucoup de diérences avec la notion  usuelle  de variable globale : 6 1. La variable est nécessairement initialisée, ici par le résultat de 1+2 (en C une variable non initialisée peut contenir n'importe quelle valeur ; en Java une variable non initialisée se voit donner une valeur par défaut, qui sera toujours la même, mais ce n'est pas la même chose qu'exiger une initialisation de la part du programmeur). 2. Le type de la variable n'a pas besoin d'être déclaré, il est inféré par le compilateur (nous verrons comment dans la section 1.3) ; ici le type inféré est int, le type des entiers relatifs. Comme tout autre compilateur, le compilateur Ocaml vérie que les expressions sont bien typées (pour rejeter des expressions telles que 1+true) mais l'utilisateur n'a pas besoin d'indiquer de types dans son programme. 3. Le contenu de la variable n'est pas modiable ; en d'autre termes, la variable x contiendra la valeur 3 jusqu'à la n du programme (nous verrons dans un instant qu'il existe aussi des variables modiables en place). 1.1.3 Références Si l'on souhaite utiliser une variable modiable en place, il faut l'introduire à l'aide du mot clé supplémentaire ref : let x = ref 1;; Une telle variable est appelée une référence. De la même manière que pour une variable immuable, elle doit être nécessairement initialisée et son type est automatiquement inféré par le compilateur. On peut alors modier le contenu de x avec la même syntaxe qu'en Pascal : x := 2;; En revanche l'accès à la valeur de x doit s'écrire !x. Voici un exemple de programme utilisant une référence : let x = ref 1;; print_int !x;; x := !x + 1;; print_int !x;; Cette syntaxe peut paraître lourde mais elle sera justiée plus loin. Rien n'empêche d'uti- liser la variable x directement, mais elle désigne alors la référence elle-même et non son contenu, pour un passage par référence par exemple. Le typage explicite cette distinc- tion, en donnant le type int ref à une référence contenant un entier. C'est exactement la même situation qu'avec les pointeurs du C, où l'on distingue le pointeur x et son contenu *x, si ce n'est que l'arithmétique de pointeurs n'est pas possible et qu'il n'existe pas d'équivalent au pointeur null. On voit donc qu'Ocaml propose deux sortes de variables : des variables modiables en place, les références, semblables à ce que l'on trouve dans les langages impératifs, mais également des variables immuables dont le contenu ne peut être modié. On ne trouve pas vraiment d'équivalent dans les langages C ou Java, où seule la notion de variable modiable en place existe  même si les mots clés const et final y permettent respectivement de déclarer une variable comme non modiable. 7 Deux remarques sont nécessaires. D'une part on constate que la précédence de la construc- tion let in est plus faible que celle de la séquence, permettant ainsi un style très compa- rable à celui de Java (ou de C). D'autre part la variable locale y n'est pas une référence mais une variable immuable : n'étant pas modiée par le programme, elle n'a pas lieu d'être une référence. Cela allège le programme et évite les erreurs. D'une manière géné- rale, il est judicieux de préférer l'utilisation de variables immuables autant que possible, car cela améliore la lisibilité du programme, sa correction et son ecacité. Cela ne veut pas dire qu'il faut tomber dans l'excès : les références sont parfois très utiles, et il serait regrettable de chercher à s'en passer systématiquement (ce que font les programmeurs Haskell). Récapitulation Pour résumer rapidement cette section, nous avons vu  qu'un programme est une suite d'expressions et de déclarations ;  que les variables introduites par le mot clé let ne sont pas modiables ;  qu'il n'y a pas de distinction entre expressions et instructions. 1.2 La boucle d'interaction ocaml Avant d'aller plus loin, nous pouvons nous arrêter sur une spécicité du langage Ocaml : sa boucle d'interaction. À côté du compilateur ocamlc dont nous avons déjà illustré l'usage, il existe un autre  compilateur  du langage, interactif celui-ci. On le lance avec la commande ocaml : % ocaml Objective Caml version 3.08.0 # et l'on se retrouve invité (par le signe # appelé justement invite en français, prompt en anglais) à entrer une expression ou une déclaration Ocaml à l'aide du clavier. Le cas échéant, la phrase est analysée syntaxiquement, typée, évaluée et le résultat est aché. # let x = 1 in x + 2;; - : int = 3 # Ici le système indique que l'expression est de type int et que sa valeur est 3. On retrouve alors l'invite, et ce indéniment. C'est pourquoi on parle de boucle d'interaction (toplevel en anglais). Si l'on entre une déclaration, celle-ci est typée et évaluée de la même manière : # let y = 1 + 2;; val y : int = 3 Ici le système indique de plus qu'il s'agit d'une variable nommée y. Les déclarations et expressions se font suite exactement comme dans le texte d'un programme. La variable y peut donc être maintenant utilisée : 10 # y * y;; - : int = 9 Cette boucle d'interaction est très utile pour apprendre le langage ou écrire de courts morceaux de code et les tester immédiatement, avec la possibilité d'examiner types et valeurs de manière immédiate. Dans la suite de ce cours, nous utiliserons souvent le résultat d'une évaluation dans le toplevel ocaml, de manière à visualiser immédiatement le type et/ou la valeur. 1.3 Fonctions Le lecteur était sans doute impatient d'en arriver aux fonctions, puisqu'il s'agit de programmation fonctionnelle. Dans un premier temps, il n'y a pas de diérence avec les autres langages de programmation : les fonctions servent à découper le code de manière logique, en éléments de taille raisonnable, et à éviter la duplication de code. 1.3.1 Syntaxe La syntaxe d'une déclaration de fonction est conforme à la syntaxe de son utilisation. Ainsi let f x = x * x dénit une fonction f ayant un unique argument x et retournant son carré. Comme on le voit ici, le corps d'une fonction n'est autre qu'une expression, qui sera évaluée lorsque la fonction sera appelée. Il n'y a pas de return comme en C ou en Java, ce qui est une fois encore cohérent avec l'absence de distinction expression/instruction. D'autre part, on note que le système infère automatiquement le type de l'argument x, ainsi que le type du résultat (et donc le type de la fonction f). Si l'on entre la déclaration ci-dessus dans le toplevel Ocaml, on obtient # let f x = x * x;; val f : int -> int = <fun> Autrement dit le système indique que l'on vient de déclarer une variable appelée f, que son type est int -> int c'est-à-dire le type d'une fonction prenant un entier en argument et retournant un entier, et enn que sa valeur est <fun> c'est-à-dire une fonction. Une telle valeur ne saurait être achée autrement, car le code n'est pas une valeur de première classe (contrairement à Lisp par exemple). Comme nous l'avons déjà vu, l'application s'écrit par simple juxtaposition. Ainsi l'ap- plication de f à 4 donne # f 4;; - : int = 16 11 Procédures et fonctions sans argument En Ocaml, une procédure n'est rien d'autre qu'une fonction dont le résultat est de type unit. On peut ainsi introduire une référence x et une fonction set pour en xer la valeur : # let x = ref 0;; # let set v = x := v;; val set : int -> unit = <fun> et la fonction set ne retourne pas (vraiment) de valeur, ainsi que l'indique son type. Elle procède par eet de bord. On peut ainsi l'appeler sur la valeur 3 et constater l'eet sur le contenu de la référence x : # set 3;; - : unit = () # !x;; - : int = 3 Inversement une fonction sans argument va s'écrire comme prenant un unique argu- ment de type unit, celui-ci se notant alors (). On peut ainsi dénir une fonction reset remettant le contenu de la référence x à 0 : # let reset () = x := 0;; val reset : unit -> unit = <fun> et l'appel à reset s'écrit reset (). Sur ce dernier point on constate que la syntaxe de la valeur () n'a pas été choisie au hasard : on retrouve la syntaxe usuelle des fonctions C ou Java sans argument. Fonctions à plusieurs arguments Une fonction ayant plusieurs arguments se déclare avec toujours la même syntaxe consistant à juxtaposer fonction et arguments : # let f x y z = if x > 0 then y + x else z - x;; val f : int -> int -> int -> int = <fun> On voit sur cet exemple que le type d'une fonction prenant trois entiers en argument et re- tournant un entier s'écrit int -> int -> int -> int. L'application d'une telle fonction emprunte toujours la même syntaxe : # f 1 2 3;; - : int = 3 Fonctions locales Comme nous le verrons plus en détail dans la section suivante, une fonction est en Ocaml une valeur comme une autre. Dès lors elle peut être déclarée localement comme une variable de n'importe quel autre type. Ainsi on peut écrire 12
Docsity logo


Copyright © 2024 Ladybird Srl - Via Leonardo da Vinci 16, 10126, Torino, Italy - VAT 10816460017 - All rights reserved