Les fonctions en OC AML Qu’est-ce qu’une fonction en OC AML ? Cours numéro 2: environnements – fonctions – fonctions récursives C’est une valeur comme une autre (au même titre qu’un int, un float ou un char). LI213 – Types et Structures de données Conséquences : une fonction a un type Christophe Gonzales – Pierre-Henri Wuillemin on peut créer des liaisons dont la valeur est une fonction (≈ affecter une fonction à un identifiant) Licence d’Informatique – Université Paris 6 on peut créer des fonctions visibles uniquement dans un environnement local etc Christophe Gonzales – Pierre-Henri Wuillemin Types des fonctions Cours numéro 2: environnements – fonctions – fonctions récursives Application de fonction Notations des types des fonctions Comment appliquer une fonction ? Vus dans le manuel de référence (core library, module Pervasives) : Écrire le nom de la fonction suivi de son (ou ses) argument(s), le tout séparé par des espaces (comme en Scheme) int of float : float -> int int of float prend en argument un float et renvoie un int Exemple : mod float de type : float -> float -> float # mod float 3.4 2.;; mod float : float -> float -> float mod float prend deux arguments (des float) et renvoie le float égal au reste de la division du premier argument par le deuxième. - : float = 1.4 Règle : le type d’une fonction est obtenu en écrivant le type de ses arguments suivi du type de retour de la fonction, le tout séparé par des -> Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives ne pas mettre les arguments entre parenthèses séparés par des «,» (voir le cours sur les n-uplets) # mod float (3.4,2.);; This expression has type float * float but is here used with type float Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Déclaration d’une fonction (1/3) Déclaration d’une fonction (2/3) let f x = x + 1 ;; Comment créer une fonction Trois manières différentes : 1 let f x = x + 1 ;; (méthode préférée) noms des paramètres : placés entre le nom de la fonction et le signe = 2 let f = function x -> x + 1 ;; chaque paramètre est placé entre un mot-clé function et une -> (moins, supérieur) Type de la fonction f : int -> int le type du premier argument, suivi d’une flèche suivie du type du deuxième argument, suivi d’une flèche, ..., suivi d’une flèche suivie du type de valeur retournée par la fonction =⇒ impossible de déclarer : # let f x = if x < 4 then 3.5 else 6;; let f = fun x -> x + 1 ;; 3 car la valeur retournée n’aurait pas un type bien défini (float si x < 4 ou int si x ≥ 4) Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Déclaration d’une fonction (3/3) Soit l’environnement E = b(x,7.6), (y,-7.8), ...c let f x y = x + y + 1 ;; # let x au plus pres = int of float (x +. 0.5) and y au plus pres = int of float (y -. 0.5);; Valeur de la fonction La valeur d’une fonction est sa fermeture notée : y Cours numéro 2: environnements – fonctions – fonctions récursives Exemple d’application de fonction Soit l’environnement E x Christophe Gonzales – Pierre-Henri Wuillemin val x au plus pres : int = 8 val y au plus pres : int = -8 x + y + 1, E Elle est caractérisée par : les noms des paramètres formels (x,y) le corps de la fonction (x + y + 1) l’environnement E dans lequel la fonction a été définie Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Évaluation : x +. 0.5 est évalué dans l’environnement E =⇒ 8.1 puis int of float est appliqué à 8.1 =⇒ l’entier 8 Pourquoi faut-il des parenthèses ? Sinon OC AML parenthèse de la manière suivante : let x au plus pres = (int of float x) +. 0.5 Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Évaluation d’une application de fonction (1/4) Évaluation d’une application de fonction (2/4) # let y = 3 ;; let f x = if y = 2 then x else x - 2 ;; let y = 2 ;; f 3;; Mécanisme d’évaluation de f x y+1, Env f) C Env fc Soit E = b(x,3),...,(f,y l’environnement dans lequel on applique f x ;; 1 x est évalué dans l’environnement courant E =⇒ 3 2 f est évaluée dans E =⇒ fermeture : y y+1, Env f 3 on crée un env local E loc = b(y,3) C Env fc : on rajoute donc à Env f une liaison entre y, le paramètre formel de f, et sa valeur (3) puis E1 = b(y,3) C E0 c 4 On évalue le corps de f (ici y + 1) dans E loc =⇒ 4 puis E2 = b(f,x 5 On retourne la valeur 4 et on détruit E loc puis E3 = b(y,2),(f,x Que vaut f 3 ? 3 ou 1 ? Indice : constituer l’environnement : Au départ, environnement E0 ..., E1 ),(y,3) C E0 c ..., E1 ),(y,3) C E0 c f 3 est évaluée dans E3 Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Évaluation d’une application de fonction (3/4) ..., E1 ),(y,3) C E0 c 1 3 est évalué dans E3 =⇒ 3 2 f est évalué dans E3 =⇒ fermeture : x if y = 2 then x else x - 2, E1 3 on crée un environnement local E loc = b(x,3) C E1 c 4 on évalue if y = 2 then x else x - 2 dans E loc ⇒ 1 5 on retourne 1 et on détruit E loc Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Évaluation d’une application de fonction (4/4) # let y = 3 ;; let f x = if y = 2 then x else x - 2 ;; let y = 2 ;; f 3;; E1 = b(y,3) C E0 c E3 = b(y,2),(f,x Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Règle : Les valeurs des «identifiants» utilisées dans le corps d’une fonction (exceptés celles des paramètres formels) sont celles que ces identifiants avaient lors de la déclaration de la fonction =⇒ vous pouvez définir de nouvelles liaisons avec des identifiants utilisés dans une fonction, cela n’a aucun impact sur la fonction. Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Les fonctions à plusieurs paramètres Application partielle (1/2) # let f x y = x + y and g = function x -> function y -> x + y and h = function x -> (function y -> x + y);; Application partielle : appliquer une fonction en lui passant ses premiers mais pas l’ensemble de ses arguments val f : int -> int -> int = <fun> val g : int -> int -> int = <fun> val h : int -> int -> int = <fun> # let f x y = x + y ;; let g = f 3 ;; g 2;; Fonctions à plusieurs paramètres function x -> function y -> x + y ⇔ function x -> (function y -> x + y) val f : int -> int -> int = <fun> val g : int -> int = <fun> - : int = 5 =⇒ une fonction à n paramètres est une fonction à un seul paramètre renvoyant une fonction à n − 1 paramètres Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Application partielle (2/2) Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Les fonctions récursives # let f x y = x + y ;; let g = f 3 ;; g 2;; # let fact x = if x = 0 then 1 else x * (fact (x-1));; E1 = b(f,x Unbound value fact 1 2 3 4 5 6 7 8 y x+y, E0 ),...c on évalue 3 dans E1 =⇒ 3 on évalue f dans E1 =⇒ x y x+y, E0 on crée E2 = b(x,3) C E0 c on évalue le corps de la fonction dans E2 =⇒ fonction y x+y dans l’environnement E2 = y x+y, E2 x+y, E2 ) C E1 c on modifie l’env : E3 = b(g,y on récupère valeur de 2 et la fermeture de g dans E3 on crée E4 = b(y,2) C E2 c on évalue x + y dans E4 =⇒ 5 Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives problème : si on veut évaluer f 3, on fait appel à f 2. Or f n’appartient pas à l’environnement de la fermeture de f ! Règle : pour déclarer une fonction récursive, utiliser let rec =⇒ rajoute une liaison entre f et sa fermeture dans l’environnement de la fermeture de f # let rec f x = if x = 0 then 1 else x * (f (x-1));; val f : int -> int = <fun> Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Les fonctions récursives imbriquées La pile d’exécution (1/2) on veut déclarer deux fonctions f et g telles que f appelle g et g appelle f =⇒ f doit appartenir à la fermeture de g et g doit appartenir à la fermeture de f Exécution d’une fonction : # let f x = x + 1;; Règle : pour déclarer des fonctions récursives imbriquées, utiliser let rec ... and ... let y = f 3 in y + 2;; # let rec pair x = if x = 0 then true else impair (x-1) and impair x = if x = 0 then false else pair (x-1) ;; pair 3;; 2 problèmes : val pair : int -> bool = <fun> val impair : int -> bool = <fun> - : bool = false Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives La pile d’exécution (2/2) 3 1 comment retourner la valeur de f 3 à y ? 2 comment savoir quelle est la prochaine instruction à exécuter après la fin de la fonction ? Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Limite de la pile d’exécution # let f x = x + 1;; let y = f 3 in y + 2;; Problème : la pile d’exécution n’a pas une taille infinie =⇒ nombre limité d’appels de fonctions Exemple # let rec f x = x + f x;; Pile d’exécution val f : int -> int = <fun> 1 copier sur la pile d’exécution les paramètres, l’adresse de la prochaine instruction à exécuter après f 3, etc 2 f utilise la pile pour la valeur des paramètres 3 f stocke la valeur à retourner dans la pile 4 la pile indique quelle liaison effectuer pour y et quelle est la prochaine instruction à exécuter 5 # f 3;; Stack overflow during evaluation (looping recursion ?). Est-ce une fatalité ? Pas en OC AML ! ! on dépile l’appel à la fonction f Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Les fonctions récursives non terminales Les fonctions récursives terminales (1/4) # let f x = let rec g somme x = g (somme + x) (x+1) in g 0 x;; Récursivité non terminale # let rec f x = x + f (x + 1);; Cette fonction calcule la même somme que celle du transparent précédent mais elle s’exécute indéfiniment. Pourquoi ? f 3 =⇒ calculer 3 + f 4 =⇒ on ne peut calculer la somme avant de connaı̂tre la valeur de f 4 =⇒ lorsque l’on exécute f 4, il reste des calculs en suspens (la somme) =⇒ on est obligé d’empiler le nouvel appel à f 4 =⇒ la pile grossit et elle explose Christophe Gonzales – Pierre-Henri Wuillemin f 3 =⇒ renvoyer g 0 3 =⇒ renvoyer g 3 4 =⇒ renvoyer g 7 5 =⇒ renvoyer g 12 6, etc Imaginons que la valeur retournée par g 12 6 soit 30 Alors val retournée par g 7 5 = val retournée par g 3 4 = 30 =⇒ inutile de stocker plusieurs fois cette valeur dans la pile d’exécution. Cours numéro 2: environnements – fonctions – fonctions récursives Les fonctions récursives terminales (2/4) Cours numéro 2: environnements – fonctions – fonctions récursives Les fonctions récursives terminales (3/4) # let f x = let rec g somme x = g (somme + x) (x+1) in g 0 x;; Lorsque g 12 6 se termine, on revient à g 7 5 qui se termine, qui revient à g 3 4 qui se termine, etc =⇒ Lorsqu’un appel à une fonction g se termine, tous les appels précédents se terminent aussi =⇒ inutile de stocker toutes les adresses de retour dans la pile d’exécution, seule celle de g 3 4 suffit Christophe Gonzales – Pierre-Henri Wuillemin Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives # let f x = let rec g somme x = g (somme + x) (x+1) in g 0 x;; Conclusion : on n’a pas besoin de conserver plusieurs valeurs de retour dans la pile on n’a pas besoin de conserver plusieurs adresses de retour 4 3 =⇒ plutôt que d’empiler les nouveaux appels à g, substituer dans la pile les informations relatives aux anciens appels de g par celles des nouveaux appels Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives Les fonctions récursives terminales (4/4) Récursivité terminale : définition Une fonction récursive f dans laquelle lorsqu’un appel à f est réalisé, aucun calcul n’est en suspens. Ces appels n’occasionnent pas d’accroissement de la taille de la pile d’exécution. Christophe Gonzales – Pierre-Henri Wuillemin Cours numéro 2: environnements – fonctions – fonctions récursives