Premiers pas Données structurées La programmation dans l’épreuve de modélisation Programmation OCaml pour l’agrégation Vincent Picard IRISA - ENS Rennes Année 2012 – 2013 Premiers pas Données structurées La programmation dans l’épreuve de modélisation Plan Premiers pas Expressions et types Environnement Expressions conditionnelles Fonctions Aspects impératifs Données structurées Listes Les tableaux Les tuples Les enregistrements Les sommes La programmation dans l’épreuve de modélisation Déroulement Bonnes habitudes Premiers pas Données structurées La programmation dans l’épreuve de modélisation Premiers pas Premiers pas Données structurées La programmation dans l’épreuve de modélisation Expressions • Un programme OCaml est grossièrement constitué d’une suite finie d’expressions qui sont évaluées une à une. • Contrairement à certains langages (C, C++, Java), il n’existe pas d’instruction en OCaml. • Chaque expression a un type qui peut être déterminé avant l’exécution du programme. • Exemples • 5 ∗ 2 + 7 est une expression de type entier int . • true est une expression de type booléen bool. • for i = 1 to 10 do print int i done est une expression de type unité unit. • On utilise deux points-virgules pour indiquer la fin d’une expression. Voici un exemple de programme : print string ”Bonjour !” ;; print newline ();; Premiers pas Données structurées La programmation dans l’épreuve de modélisation Aperçu des types usuels • Les types de base • Unité : unit (une seule valeur ()) • Booléens : bool (ex : true, false ) • Entiers : int (ex : 42) • Nombres à virgule flottante : float (ex : 1.618) • Caractères : char (ex : ’G’) • Chaı̂nes de caractères : string (ex : ” alice ”) • Listes : ’a list (ex : [1; 1; 2; 3; 5; 8]) • Tableaux : ’a array (ex : [|’ b ’; ’o ’; ’b ’|] ) • Exceptions : exn (ex : Not found) • Les types fonctionnels : ’a −> ’b • Les types construits • n-uplets • enregistrements ou produits • unions ou sommes Premiers pas Données structurées La programmation dans l’épreuve de modélisation Manipulation basique des expressions arithémiques et booléennes • Entiers • Opérateurs : +, −, ∗, /, mod, <, <=, =, . . . • Fonctions : abs, min, max • Pas d’exponentiation... • Flottants • Opérateurs : +., −., ∗., /., ∗∗, <, <=, =, . . . • Fonctions : sqrt, exp, log, log10, cos, . . ., acos, . . ., cosh, . . . • Conversions : float of int , int of float , . . . • Booléens • Opérateurs : conjonction &&, disjonction || • Fonctions : négation not • Attention à ne pas utiliser or qui est caduc et and qui a un autre sens. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Environnements global et locaux • Une expression est évaluée dans un environnement qui lie chaque identificateur à une valeur. • Exemple : a ∗ 2 est une expression de type entier dont l’évaluation sera 4 si a est lié à la valeur 2 et 0 si a est lié à la valeur 0. • Un programme est évalué dans l’environnement global. On manipule l’environnement global grâce au mot clef let (soit en anglais) : let identificateur = expression ;; • On peut créer un environnement local pour évaluer une expression grâce aux mots clefs let et in : let identificateur = expression1 in expression2 ;; Si identificateur est déjà lié dans l’environnement courant, il sera masqué par ce nouveau lien dans l’évaluation de expression2 mais les anciens liens seront toujours présents. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Environnements global et locaux : exemple let a = 3;; let b = 4;; print int (a + b);; let c = 10 in print int (c + 3);; print int c ;; let a = 4 in print int a ;; let b = 1 in let c = b + b in print (a ∗ b ∗ c );; print int b ;; Premiers pas Données structurées La programmation dans l’épreuve de modélisation Expressions conditionnelles • Le branchement conditionnel est une opération fondamentale des programmes informatiques. • En OCaml, il prend la forme d’expressions conditionnelles : if expression test then expression1 else expression2 • expression test doit-être de type bool. • expression1 et expression2 doivent être du même type. • Le type de l’expression conditionnelle est celui de expression1 (ou expression2 ). • Sémantique : expression test est évalué ; s’il est évalué à true , expression1 est évalué et la valeur de l’expression conditonnelle est celle de expression1 , sinon c’est expression2 qui est évaluée. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Expressions conditionnelles : exemples let a = −3;; if a >= 0 then print string ” positif ” else print string ” strictement negatif ” ;; let b = 11 + if a > 0 then a else −a;; Premiers pas Données structurées La programmation dans l’épreuve de modélisation Fonctions : base • OCaml est un langage fonctionnel : la fonction est l’objet central du langage. • Type d’une fonction : ’a −> ’b est le type d’une fonction prenant comme argument une expression de type ’a et dont l’évaluation donne une valeur de type ’b. • Remarque : ’a et ’b peuvent être n’importe quels types, y compris des fonctions (on parle de First-class functions). • Appel de fonction : soit f une expression de type ’a −> ’b (une fonction), et expression de type ’a, f expression est une expression de type ’b dont la valeur est l’application de la valeur de f à la valeur de expression . • Exemple : carre 6 vaut 36 Premiers pas Données structurées La programmation dans l’épreuve de modélisation Fonctions : base • Définition d’une fonction : une fonction est définie en utilisant le mot clef function : let identifiant = function arg −> expression par exemple : let carre = function x −> x ∗ x • Remarque 1 : function est l’équivalent de λ en lambda-calcul. • Remarque 2 : À chaque appel de fonction, expression est évaluée dans un environnement local où arg est lié à la valeur passée en paramètre. • Il est recommandé d’utiliser la syntaxe simplifiée : let identifiant arg = expression Par exemple : let carre x = x ∗ x Premiers pas Données structurées La programmation dans l’épreuve de modélisation Fonctions : récursivité • Une fonction est récursive lorsqu’elle est appelée dans sa propre définition. • En OCaml, on utilise le mot clef rec après let pour que la fonction soit liée dans l’expression qui la définit. • Exemple classique : let rec factorielle n = if n <= 0 then 1 else n ∗ ( factorielle (n − 1)) ;; Premiers pas Données structurées La programmation dans l’épreuve de modélisation Fonctions : plusieurs arguments • En OCaml, on construit une fonction à n arguments par une fonction à 1 argument renvoyant la fonction partielle des n − 1 autres arguments (etc. . .), c’est la curryfication. • Exemple : norme d’un vecteur (x, y) let norme = function x −> function y −> sqrt (x ∗. x +. y ∗. y) • Le type de norme est alors float −> float −> float avec associativité à droite ( float −> (float −> float)). • Un appel de fonction possible est norme 3 5 qui peut se lire (norme 3) 5 : l’associativité se fait à gauche. (Remarque : norme 3 est aussi un appel valide) • Il est recommandé d’utiliser la syntaxe simplifiée : let norme x y = sqrt (x ∗. x +. y ∗. y) • Le mot clef fun sert également à écrire facilement des fonctions curryfiées : let norme = fun x y −> sqrt (x ∗. x +. y ∗. y) Premiers pas Données structurées La programmation dans l’épreuve de modélisation Fonctions : un exemple (∗g(x) = 2xˆ2 + 3x + 5 ∗) let g x = 2. ∗. x ∗∗ 2. +. 3. ∗. x +. 5.;; (∗ Operateur de derivation ∗) let epsilon = 1e−5;; let d f x = ((f (x +. epsilon )) −. (f x)) /. epsilon ;; let g’ = d g;; let g ’’ = d g ’;; print float (g’ 1.);; (∗ Resultat : 7.00002000009 ∗) print float (g ’’ 3.14);; (∗ Resultat : 3.99992927669 ∗) • Cet exemple illustre la puissance de la curryfication. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Aspects impératifs • OCaml est un langage multi-paradigme et permet une programmation de style impératif. • Classiquement, un programme impératif est une suite d’instructions exécutées une à une. • En OCaml, certaines expressions peuvent être assimilées à des instructions. Elles n’ont pas de valeur à proprement parler mais elles ont un effet sur l’état du système : • Affichage d’un message à l’écran • Ecriture dans un fichier • Changement de l’état mémoire (cf tableaux, références, champs mutable) On parle d’effets de bord. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Aspects impératifs • On se sert du type unit (possédant une seule valeur () ) pour typer le résultat d’une fonction ne renvoyant rien. De même on utilisera le type unit pour écrire une fonction ne prenant aucun argument. • Exemples : • print string est de type string −> unit • print newline est de type unit −> unit • Syntaxe pour écrire une fonction ne prenant pas d’argument : let rire () = print string ”Hahahahaha” ;; (∗ type : unit −> unit ∗) rire ();; Premiers pas Données structurées La programmation dans l’épreuve de modélisation Aspects impératifs : opérateur de séquencement • Lorsqu’on veut écrire une suite d’ “instructions”, on se sert de l’opérateur de séquencement : expression1 ; expression2 est une expression du même type que expression2 . • Sémantique : expression1 est évaluée et sa valeur est ignorée, puis expression2 est évaluée et sa valeur est retournée. • Remarque : le séquencement n’est donc utile que si l’évaluation de expression1 produit un effet de bord. • Associativité à droite : expr1; expr2; expr3 ;; est équivalent à expr1; (expr2; expr3 );; . Seule la valeur de la dernière expression est retournée. • Attention : ne pas confondre avec ;; qui est un terminateur. • Exemple : let rire mechant () = print string ”Mouhahaha”; print newline ();; Premiers pas Données structurées La programmation dans l’épreuve de modélisation Aspects impératifs : références • L’utilisation de ”données modifiables” simplifie parfois l’écriture de certains progammes. • OCaml introduit la notion de référence qui est une valeur permettant la lecture et l’écriture dans la mémoire d’une donnée. • Une référence a un type : elle ne permet de lire/écrire que une donnée de ce type. • Utilisation • Création : let ma ref = ref valeur initiale ;; : créé une référence de même type que valeur initiale contenant initialement la valeur de valeur initiale • Lecture : !ma ref (! se prononce bang) : renvoie la valeur contenue dans la référence • Ecriture : maref := nouvelle valeur (expression de type unit) • Pratique : incr et decr pour incrémenter et décrémenter une référence sur un entier. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Aspects impératifs : la boucle for • Syntaxe for id = debut (to|downto) fin do expr done • Cette expression est de type unit • expr devrait être de type unit et provoquer un effet de bord. • id est lié localement dans expr pour toutes les valeurs entières entre debut et fin . • On utilisera largement for pour parcourir les tableaux (et les matrices). Premiers pas Données structurées La programmation dans l’épreuve de modélisation Aspects impératifs : la boucle while • Syntaxe while condition do expr done • Cette expression est de type unit • expr devrait être de type unit et provoquer un effet de bord. • condition est une expression booléenne évaluée avant chaque itération : si elle est vraie on exécute la boucle sinon non. • En général, condition fait référence à des données en mémoire qui sont modifiées par effet de bord dans expr. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Aspects impératifs : exemple let racine pas x = let estimation = ref 0. in while (! estimation ∗∗ 2. < x) do estimation := ! estimation +. pas; done; ! estimation ;; let racine pas x = let rec cherche approx = if (approx ∗∗ 2. < x) then cherche (approx +. pas) else approx in cherche 0. ;; • Une programmation impérative peut rendre certains Premiers pas Données structurées La programmation dans l’épreuve de modélisation Données structurées Premiers pas Données structurées La programmation dans l’épreuve de modélisation Les listes • Les listes servent à manipuler un ensemble ordonné de données de même type. Seul le premier élément est accessible en temps constant. Il est facile d’ajouter un élément dans une liste. • Type : ’a list • Construction • directe : [3; 5; 7], [] (liste vide) • constructeur (::) : tete :: queue • tete est un élément de type ’a • queue est un élément de type ’a list • Exemple : 1.5::[2.4; 1.0] est la liste [1.5; 2.4; 1.0] • concaténation liste1 @ liste2 . Coût = longueur de liste1 . • Manipulation • Le filtrage • Obtenir la • Obtenir la • Obtenir la par motifs (pattern matching) tête : List .hd queue d’une liste non vide : List . tl longueur : List . length Premiers pas Données structurées La programmation dans l’épreuve de modélisation Le filtrage par motifs • Le filtrage par motifs est un mécanisme permettant de décomposer facilement des données structurées et de choisir les actions appropriées en fonction des données. • Syntaxe (c’est une expression) match expression with | motif1 −> expr1 | motif2 −> expr2 | ... | motifn −> exprn • Sémantique : expression est comparée à chaque motif dans l’ordre. Dès qu’un motif correspond à expression , l’expression résultat associée est retournée. • Remarque : comme pour le if , expr1, . . .expr2 doivent être de même type. • Le filtrage doit être exhaustif : tous les cas doivent être traités. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Le filtrage par motifs : les listes • motif tete :: motif queue est un exemple de motif décomposant une liste. La queue étant elle-même une liste, il y a donc récursivité des motifs. • Exemples Syntaxe simplifiée let rec somme l = match l with | [] −> 0 | t :: q −> t + (somme q) ;; let rec somme = function | [] −> 0 | t :: q −> t + (somme q) ;; • Les identifiants utilisés dans un motif sont liés dans l’expression associée par le filtrage. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Le filtrage par motifs : les listes • (tiret bas) est le motif universel : il filtre n’importe quelle expression : let tete l = match l with | t :: −> t | [] −> failwith ” liste vide” ;; • Utilisation de la récursivité des motifs et d’un autre type de motif de listes. let somme3 = function | a :: b :: c :: −> a + b + c | [a; b] −> a + b | [a] −> a | [] −> 0 ;; Premiers pas Données structurées La programmation dans l’épreuve de modélisation Les tableaux • Un tableau sert à représenter un nombre fixe de données de même type et ordonnées. Chaque donnée est accessible en temps constant. • ’a array • Construction • Directe : [| ”un”; ”deux”; ” trois ” |] • Bibliothèque : Array.make taille valeur initiale • Les tableaux se manipulent par effets de bord : • Lecture : t .(5) (indexation à partir de 0) • Modification : t .(5) <− 3 de type unit • Taille : Array. length t • Les chaı̂nes de caractères sont équivalentes à des tableaux de caractères mais on utilise les crochets [] pour lire et modifier une lettre. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Les tableaux : les matrices • Les matrices sont des tableaux de tableaux. • Elles doivent être créées impérativement par la commande : Array.make matrix lignes colonnes valeur initiale . • Pourquoi Array.make lignes (Array.make colonnes valeur initiale ) ne marche pas ? Écrivez une version correcte de make matrix. • Lecture : m.(i ).( j ) . Premiers pas Données structurées La programmation dans l’épreuve de modélisation Les tuples • Ils servent à manipuler les couples, triplets, . . . • Type : ’a ∗ ’b ∗ ’c ∗ . . . • Création : • (5, 6) de type int ∗ int • (”Age”, 35) de type string ∗ int • Lecture : • Pour les couples seulement : fst , snd • Filtrage par motif : match triplet with (a, b, c) −> ... • Filtrage par motif (syntaxe simplifiée) : let (a, b, c) = triplet in ... • Exemple type complexe = float ∗ float ;; let module complexe = let (x, y) = complexe in (x ∗∗ 2. +. y ∗∗ 2.) ∗∗ 0.5 ;; Premiers pas Données structurées La programmation dans l’épreuve de modélisation Les enregistrements • Ils sont équivalents aux tuples mais les données sont étiquetées. • Voici un exemple : type complexe = {re: float ; im: float };; let z = {re = 3.; im = 4.};; let module z = (z.re ∗∗ 2. + z.im ∗∗ 2.) ∗∗ 0.5 in module z;; • Noter la différence entre la création du type et d’une valeur de ce type. • Les motifs d’enregistrement peuvent être lacunaires : let partie reelle = function | {re = x} −> x ;; (le filtrage est toujours exhaustif !) • Possibilité de rendre les champs mutable (modifiable avec <−). Premiers pas Données structurées La programmation dans l’épreuve de modélisation Les sommes ou unions • Les sommes permettent de manipuler des données pouvant prendre des types différents. • Elles sont utilisées pour manipuler les arbres et pluq généralement les structures définies par induction. • Exemple type arbre bin = Feuille | Noeud of arbre binaire ∗ arbre binaire ;; Feuille ;; Noeud (Noeud (Feuille, Feuille ), Feuille );; • Les sommes sont manipulées par filtrage : let rec taille = function | Feuille −> 1 | Noeud (g, d) −> 1 + (taille g) + ( taille d) ;; Premiers pas Données structurées La programmation dans l’épreuve de modélisation Le polymorphisme, c’est pas sorcier • Fonction polymorphe : elle manipule un ensemble de types. Exemple : List . length manipule les listes d’entiers, de caractères, . . .Elle est de type ’a list −> int. ’a est une ”variable muette de type”. • L’inférence de type de OCaml choisira toujours le type le plus général. Exemple : let rec taille = function [] −> 0 | :: q −> 1 + (taille q );; est de type ’a list −> int. • Le type liste est paramétré. On peut construire un type paramétré comme ceci : type (’a, ’b) arbre etiquete = | Feuille of ’a | Noeud of ((’a ,’ b) arbre etiquete ) ∗ ’b ∗ ((’ a, ’b) arbre etiquete ) ;; Premiers pas Données structurées La programmation dans l’épreuve de modélisation Exercices Proposez un (ou des) type(s) adapté(s) pour représenter • les coordonnées d’un point ; • un graphe ; • une formule de la logique propositionnelle ; • une fiche d’informations personnelles (nom, prénom, . . .) ; • un arbre ternaire ; • une séquence ADN (suite de symboles A, T, G, C). Premiers pas Données structurées La programmation dans l’épreuve de modélisation La programmation dans l’épreuve de modélisation Premiers pas Données structurées La programmation dans l’épreuve de modélisation L’exercice de programmation • Il est obligatoire. • Vous présenterez votre programme pendant environ 10min/40min au moment où vous le voudrez. Au bout de 30min le jury vous demandera de présenter votre programme. • Comme le reste du concours, c’est une épreuve très codifiée. Il est très facile de gagner des points... il est aussi facile d’en manquer. • Suivre le plan suivant : 1. argumenter sur le choix du langage de programmation ; 2. argumenter sur les types choisis pour représenter les données manipulées. (surligner les types) ; 3. expliquer chaque fonction une à une (en les surlignant) ; 4. pour chaque fonction exécuter les exemples prévus ; 5. pour terminer, donner les complexités de vos fonctions. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Pendant la préparation • Commencer l’exercice de programmation dès que votre compréhension du sujet le permet. • Bien réfléchir aux types de données, à la structure. • Prévoir des exemples pertinents d’exécution et les inclure. • Vérifiez vos programmes au fur et à mesure grâce aux exemples. • Enregistrer votre travail régulièrement. • À la fin, relancer Emacs, puis tout recompiler (si possible en pensant à ce qu’on va dire). • Votre code doit être lisible et compréhensible au premier coup d’œil. Premiers pas Données structurées La programmation dans l’épreuve de modélisation Comment choisir les types biens ? • Règles • Le type devrait pouvoir représenter au mieux le domaine des données qu’on manipule : ni plus, ni moins. • Le type doit être adapté aux opérations utilisées. • Conseils • Éviter la bidouille ! • Tableau ou liste ? : taille variable ? accès direct aux éléments ? • Exemples : • Arc avec poids positifs ou absence d’arc entre deux sommets : type arc = int ;; (∗ −1 si absence d’ arc ∗) NON type arc = Arc of int | Absence;; OUI • Ensemble de n interrupteurs ON/OFF dont on change les états : type leviers = bool list ;; NON type leviers = bool array ;; OUI Premiers pas Données structurées La programmation dans l’épreuve de modélisation Comment rendre mon code lisible ? • Indentation : elle doit correspondre à la ”logique” de l’algorithme. • Écrire de nombreuses petites fonctions espacées. • Inclure quelques commentaires là où c’est nécessaire. Trop de commentaires tue le commentaire. • Choisir le bon paradigme (impératif/fonctionnel) : coder en utilisant le paradigme naturel dans lequel l’algorithme s’exprimme. • Les identifiants • Choisissez une langue et gardez ce choix. • Préférez la syntaxe voila un identifiant . • Ne pas hésiter à choisir un identifiant un peu long s’il permet de comprendre le sens de l’objet désigné. Exemple : nombre de sommets plutôt que nbsom.