Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • programmation-fonctionnelle/lifpf
  • p2208151/lifpf
  • p2402635/lifpf
  • p2103541/lifpf
  • p2105194/lifpf
  • p2210488/lifpf
  • p2212652/lifpf
  • p2019985/lifpf
  • p2208660/lifpf
  • p2102825/lifpf
  • p2002599/lifpf
  • p2106670/lifpf
  • p2103270/lifpf
  • p2210897/lifpf-demo
  • p2110686/lifpf-aysoy
  • p2110638/lifpf
  • p2202556/lifpf
17 results
Show changes
# TP5: Parcours génériques de structure
## 1. Manipulations de listes à l'aide de fonctions génériques
On considère les fonctions suivantes prédéfinies en OCaml, voir [la documentation du module `List`](https://v2.ocaml.org/api/List.html) :
- **`List.map: ('a -> 'b) -> 'a list -> 'b list`** Cette fonction transforme une liste en utilisant la fonction passée en paramètre pour transformer les éléments un par un. Ainsi `List.map f [x1; x2; x3; ... ]` renverra une liste égale à `[f x1; f x2; f x3; ... ]`.
- **`List.filter: ('a -> bool) -> 'a list -> 'a list`** Cette fonction transforme une liste en utilisant la fonction passée en paramètre conserver uniquement certains éléments. Ainsi `List.filter f [x1; x2; x3; ... ]` renverra une liste contenant exactement les `xi` pour lesquels `f xi` vaut `true`.
- **`List.for_all: ('a -> bool) -> 'a list -> bool`** Cette fonction indique si une fonction renvoie vrai pour tous les éléments d'une liste. Ainsi `List.for_all f [x1; x2; x3; ... ]` renvoie `true` si pour tous les `xi`, on a `f xi` vaut `true`.
## 1.1. Utilisation des fonctions sur les listes
> En utilisant `List.map`, coder les fonctions suivantes :
>
> - Une fonction `f1` qui prend une liste d'`int` et renvoie la liste de leur représentation sous forme de `string`.
> - Une fonction `f2` qui prend un `int` `x` et une liste d'`int` `l` et renvoie la liste transformée en multipliant chaque élément de `l` par `x`.
> - Une fonction `f3` qui prend deux `int` `x` et `y` et une liste d'`int` `l` et renvoie la liste (de `bool`) indiquant pour chacun des éléments de `l` s'il est bien compris entre `x` et `y`.
Bien tester vos fonctions avec `assert`.
> En utilisant `List.filter`, coder les fonctions suivantes :
>
> - Une fonction `f4` qui prend deux `int` `x` et `y` et une liste d'`int` `l` et renvoie la liste des éléments de `l` qui sont compris entre `x` et `y`.
> - Une fonction `f5` qui prend une liste de liste d'`int` et renvoie la liste sans les (sous-)listes vides.
Bien tester vos fonctions avec `assert`.
En OCaml, on peut voir un opérateur comme une fonction en le plaçant tout seul entre parenthèses. Ainsi `(!=)` est une fonction qui prend deux arguments et renvoie `true` s'ils sont différents. Ainsi `(!=) 1 2` renvoie `true`, alors que `(!=) 1 1` renvoie `false`.
> Recoder la fonction `f5` sans le mot clé `fun` en utilisant une application partielle de `(!=)`.
On peut bien sûr combiner `List.map` et `List.filter`.
> Coder une fonction `f6` qui prend une liste d'`int option` `l` et renvoie une liste d'`int` contenant les valeurs `int` contenues dans `l`. Par exemple, `f6 [ Some 3; None; Some 18; Some 42; None; Some 37; None]` vaudra `[3; 18; 42; 37]`.
## 1.2 Recodage de fonctions sur les listes
> Recoder `List.map`, `List.filter` et `List.for_all`.
**Faire valider votre progression par votre chargé de TP**
## 2. Arbre n-aires: recodage
Dans cet exercice, on reprend les fonctionnalités développées dans la section 1. du [TP4](tp4.md), mais en les recodant avec les fonctions déjà fournies avec OCaml.
Remarque : en OCaml, on peut écrire `fun x y -> ...` à la place de `fun x -> fun y -> ...`.
### 2.1. Recodage de quelques fonctions de base avec la bibliothèque standard OCaml
> Reprendre le type `'a arbre_n` du TP4, section 1.1
On pourra reprendre les tests écrits lors du TP4.
> Recoder la fonction `hauteur_arbre` sans utiliser `hauteur_foret`, mais en appelant directement `List.map` et `List.fold_left` pour extraire les hauteurs des arbres fils et trouver la hauteur maximale parmi celles-ci.
> Recoder `list_of_arbre_aux` en utilisant `List.fold_right` pour remplacer les appels à `list_of_foret`.
> Encapsuler `list_of_arbre_aux` avec un `let ... in ...` dans la définition de `list_of_arbre`.
> On écrira le `let` avant de prendre l'argument de `list_of_arbre`.
**Faire valider votre progression par votre chargé de TP**
### 2.2. Gestion d'option, fold et minimum
Pour gérer proprement le calcul du minimum, on va s'équiper d'un décorateur `lift_option_2` ayant le comportement suivant. Soit `f: 'a -> 'a -> 'a`. On suppose que `g = lift_option_2 f`. Alors `g` aura le type `'a option -> 'a -> 'a option`. Si le premier argument de `g` est `None`, alors `g` renvoie `Some x` si `x` est son second argument. Sinon `g` renvoie `Some (f y x)``y` est la valeur contenue dans son premier argument et `x` est son second argument.
> Définir `lift_option_2` en commençant par préciser son type. Tester avec `assert` en passant la fonction `min` à `lift_option_2`.
On veut à présent définir `fold_left_arbre` qui agrège une valeur via un accumulateur à la manière de `List.fold_left`, mais en parcourant un arbre et pas une liste.
> Définir le type, puis coder `fold_left_arbre`. On pourra utiliser intelligemment `List.fold_left` pour gérer le cas des `Noeud`.
> Tester en calculant la somme des éléments d'arbres bien choisis.
On peut remarquer que `reduce` du TP4 ressemble énormément à `fold_left_arbre`: les deux vont parcourir les éléments de l'arbre les combinant via un accumulateur. Elles diffèrent cependant sur les points suivants:
- `reduce` prend en argument une fonction de type `'a -> 'a -> 'a` alors que `fold_left_arbre` prend une fonction un peu plus générique de type `'b -> 'a -> 'b`.
- `reduce` renvoie forcément une `option` alors que `fold_left_arbre` renvoie uniquement une option si `'b` est lui-même une `option`.
> En utilisant intelligemment `fold_left_arbre`, recoder `reduce`.
> Recoder `list_of_arbre` en utilisant `fold_left_arbre`.
**Faire valider votre progression par votre chargé de TP**
(**********************************************************************)
(* 1 Manipulations de listes à l'aide de fonctions génériques *)
(**********************************************************************)
(**********************************************************************)
(* 1.1. Utilisation des fonctions sur les listes *)
(** Une fonction `f1` qui prend une liste d'`int` et renvoie la liste de leur représentation sous forme de `string`. *)
let f1 (l : int list) : string list = List.map (fun x -> string_of_int x) l
(* ou bien *)
let f1 = List.map string_of_int;;
assert (f1 [ 1; 2 ] = [ "1"; "2" ])
(** Une fonction `f2` qui prend un `int` `x` et une liste d'`int` `l` et renvoie la liste transformée en multipliant chaque élément de `l` par `x`. *)
let f2 (x : int) (l : int list) : int list = List.map (fun y -> x * y) l
(* ou bien *)
let f2 (x : int) : int list -> int list = List.map (fun e -> x * e);;
assert (f2 3 [ 1; 5; 3 ] = [ 3; 15; 9 ])
(** Une fonction `f3` qui prend deux `int` `x` et `y` et une liste d'`int` `l` et renvoie la liste (de `bool`) indiquant pour chacun des éléments de `l` s'il est bien compris entre `x` et `y`. *)
let f3 (x : int) (y : int) (l : int list) : bool list =
List.map (fun e -> x <= e && e <= y) l
(* ou bien *)
let f3 (x : int) (y : int) : int list -> bool list =
List.map (fun e -> x <= e && e <= y)
;;
assert (f3 3 7 [ 1; 4; 2; 12 ] = [ false; true; false; false ])
(** Une fonction `f4` qui prend deux `int` `x` et `y` et une liste d'`int` `l` et renvoie la liste des éléments de `l` qui sont compris entre `x` et `y`. *)
let f4 (x : int) (y : int) (l : int list) : int list =
List.filter (fun e -> x <= e && e <= y) l
(* ou bien *)
let f4 (x : int) (y : int) : int list -> int list =
List.filter (fun e -> x <= e && e <= y)
;;
assert (f4 3 7 [ 1; 4; 2; 12 ] = [ 4 ])
(** Une fonction `f5` qui prend une liste de liste d'`int` et renvoie la liste sans les (sous-)listes vides. *)
let f5 (l : int list list) : int list list = List.filter (fun l' -> l' != []) l
(* variante avec (=) *)
let f5 : int list list -> int list list = List.filter (( != ) []);;
assert (f5 [ []; [ 2; 3 ]; []; [ 1 ] ] = [ [ 2; 3 ]; [ 1 ] ])
(** Coder une fonction `f6` qui prend une liste d'`int option` `l` et renvoie une liste d'`int` contenant les valeurs `int` contenues dans `l`. Par exemple, `f6 [ Some 3; None; Some 18; Some 42; None; Some 37; None]` vaudra `[3; 18; 42; 37]`. *)
let f6 (l : int option list) : int list =
let l1 = List.filter (fun o -> o != None) l in
List.map
(fun o ->
match o with
| None -> 0 (* n'arrive pas à cause du filter précédent *)
| Some x -> x)
l1
(* ou bien *)
let f6 (l : int option list) : int list =
l
|> List.filter (( != ) None)
|> List.map (fun o ->
match o with
| None -> 0 (* n'arrive pas à cause du filter précédent *)
| Some x -> x)
(* ou encore en utilisant Option.get *)
let f6 (l : int option list) : int list =
l |> List.filter (( != ) None) |> List.map Option.get
;;
assert (
f6 [ Some 3; None; Some 18; Some 42; None; Some 37; None ] = [ 3; 18; 42; 37 ])
(**********************************************************************)
(* 1.2. Recodage de fonctions sur les listes *)
let rec map (f : 'a -> 'b) (l : 'a list) : 'b list =
match l with [] -> [] | x :: l' -> f x :: map f l'
let rec filter (f : 'a -> bool) (l : 'a list) : 'a list =
match l with
| [] -> []
| x :: l' -> if f x then x :: filter f l' else filter f l'
let rec for_all (f : 'a -> bool) (l : 'a list) : bool =
match l with [] -> true | x :: l' -> f x && for_all f l'
(**********************************************************************)
(* 2 Arbre n-aires: recodage *)
(**********************************************************************)
(**********************************************************************)
(* 2.1. Recodage de quelques fonctions de base avec la bibliothèque
standard OCaml *)
type 'a arbre_n = Feuille of 'a | Noeud of 'a arbre_n list
(**
Calcule la hauteur d'un arbre.
@param a l'arbre dont on veut calculer la hauteur
@return la hauteur de l'arbre
*)
let rec hauteur_arbre (a : 'a arbre_n) : int =
match a with
| Feuille _ -> 1
| Noeud foret ->
let hauteurs = List.map hauteur_arbre foret in
let h_max = List.fold_left max 0 hauteurs in
h_max + 1
(**
Extrait les éléments d'un arbre dans une liste
@param l'arbre dont on veut les éléments
@return la liste des éléments de l'arbre obtenue par un parcours en profondeur.
*)
let list_of_arbre : 'a arbre_n -> 'a list =
(*
Ajoute les éléments de l'arbre à la liste.
@param a l'arbre dont on veut extraires les éléments
@param acc la liste à laquelle on veut ajouter les éléments.
*)
let rec list_of_arbre_aux (a : 'a arbre_n) (acc : 'a list) : 'a list =
match a with
| Feuille x -> x :: acc
| Noeud foret ->
List.fold_left (fun acc2 fils -> list_of_arbre_aux fils acc2) acc foret
in
fun a -> list_of_arbre_aux a []
(**********************************************************************)
(* 2.2. Gestion d'option, fold et minimum *)
(**
[lift_option_2 f] choisi son deuxième argument si son premier argument est None.
Sinon utilise f pour produire une valeur avec ses deux arguments.
@param f la fonction utilisée pour combiner les arguments
@param x une option
@param y la valeur à combiner à la valeur de x ou à prendre si x est None
@return Some de la combinaison des valeurs de x et y, ou bien y si x est None
*)
let lift_option_2 (f : 'a -> 'a -> 'a) : 'a option -> 'a -> 'a option =
fun x y -> match x with None -> Some y | Some x' -> Some (f x' y)
(**
aggrège une valeur en utilisant un accumulateur et une fonction appelée pour
mettre à jour l'accumulateur en fonction de l'élément de l'arbre rencontrée.
Appelle la fonction en utilisant les un après les autres tous les éléments de
l'accumulateur.
@param f la fonction de mise à jour de l'accumulateur
@param init la valeur de départ de l'accumulateur
@param a l'arbre à parcourir
@return la valeur de l'accumulateur résultant des mises à jour
successives par les appels à f sur les éléments de a.
*)
let rec fold_left_arbre (f : 'b -> 'a -> 'b) (init : 'b) (a : 'a arbre_n) : 'b =
match a with
| Feuille x -> f init x
| Noeud foret -> List.fold_left (fold_left_arbre f) init foret
(* Pour le dernier cas on aurait pu écrire
List.fold_left (fun acc fils -> fold_left_arbre f acc fils) init foret
mais c'est plus long
*)
(**
Aggrège une valeur en utilisant une fonction de combinaison de valeurs
@param f la fonction de combinaison de valeurs
@param a l'arbre dont on veut combiner les valeurs
@return Some du résultat de la combinaisons des valeurs de a par f,
ou None si a n'a pas d'élément
*)
let reduce (f : 'a -> 'a -> 'a) (a : 'a arbre_n) : 'a option =
fold_left_arbre (lift_option_2 f) None a
(**
Extrait les éléments d'un arbre dans une liste
@param l'arbre dont on veut les éléments
@return la liste des éléments de l'arbre obtenue par un parcours en profondeur.
*)
let list_of_arbre' : 'a arbre_n -> 'a list =
fold_left_arbre (fun l e -> e :: l) []
# TP6 : Parcours et transformations de structures
**Attention** dans les sujets qui suivent, il est demandé de ne **pas regarder le corrigé** pour faire les exercices.
Lire un corrigé et (re)faire l'exercice seul (sans regarder une correction) sont deux choses très différentes.
**Votre chargé de TP validera votre progression en fin de séance.**
## 1. Manipulations de listes
Coder l'exercice 1 du [TD5](../td/lifpf-td5-enonce.pdf), en faisant en priorité ce qui n'a pas pu être fait en séance de TD.
## 2. Parcours et transformation d'arbres de recherche
Coder l'exercice 3 du [TD4](../td/lifpf-td4-enonce.pdf)
## 3. Calculatrice
Terminer complètement le codage de la calculatrice du [TP3](tp3.md).
# TP7 - Application et configuration par fonctions
On va implémenter une petite application simple en OCaml.
Même si l'application tiendra en un seul fichier, elle comprendra plusieurs parties regroupées en _modules_.
## 1. Application et modules
On souhaite implémenter une application de gestion de la fabrication des jouets de Noël par les lutins du Père-Noël.
Bien que l'application soit modeste on souhaite pouvoir la faire grossir selon les besoins.
On va donc dès le départ la diviser en différents modules, le code de l'application contenant simplement des appels aux bonnes fonctions des autres modules.
Les différentes parties de l'application intiale sont les suivantes:
- Un module `Association` de gestion des associations clé-valeur basé d'abord sur des listes, puis sur des arbres binaires de recherche. On veut pouvoir, à terme (mais pas dans ce TP), remplacer les usages de ce module par un module de la bibliothèque standard OCaml.
- Un module `Usine` contenant les types liés au métier de l'application: lutins, jouets, etc. Ce module contiendra également les différentes fonctions utilisées pour gérer l'usine.
- Un module `LutinsApp` qui va contenir le code de gestion des arguments en ligne de commande
- Enfin le code hors de ces modules lancera simplement l'application.
Tous ces modules seront placés dans le fichier `usinelutins.ml`.
On peut résumer les dépendances simples des modules de cette application via le diagramme suivant (la flèche signifie "est utilisé par"):
```mermaid
graph TD
A[Association] --> B[Usine]
B --> C[LutinsApp]
C --> D[code directement dans usinelutins.ml]
```
### 2. Premiers modules
Créer un fichier `usinelutins.ml`. À titre d'exemple de module on donne le code suivant, à placer au début du fichier `usinelutins.ml`:
```ocaml
module Association = struct
(* Type pour représenter des dictionnaires (ou map ou association) dont la clé est une string *)
type 'a t = (string * 'a) list
(* Fonction pour chercher dans un dictionnaire. get d k renvoie None si k n'est pas une clé de d,
Some x si x est la valeur associée à k dans d. *)
let get : 'a t -> string -> 'a option = fun d k -> List.assoc_opt k d
(* Ajoute un couple clé/valeur dans un dictionnaire, si la clé est déjà dans le dictionnaire,
change en v la valeur qui sera renvoyée par get *)
let put : 'a t -> string -> 'a -> 'a t = fun d k v -> (k, v) :: d
(* Le dictionnaire vide *)
let empty : 'a t = []
(* Supprime les valeurs associées à la clé k dans le dictionnaire. *)
let rec delete : 'a t -> string -> 'a t =
fun d k ->
match d with
| [] -> []
| (k', v') :: rest ->
if k = k' then delete rest k else (k', v') :: delete rest k
end
```
Créer un module `Usine` à la suite du module `Association`. Dans ce module, créer:
- un type `jour` pour représenter les jours de la semaine (`Lundi`, `Mardi`, etc)
- une fonction `string_of_jour: jour -> string`
- une fonction `jour_opt_of_string: string -> jour option`
> Ajouter des `assert` pour tester `string_of_jour` et `jour_opt_of_string`.
### 3. Utilisation du module `Usine`
On souhaite maintenant utiliser ce module. Pour cela il faut:
1. Écrire le code qui l'utilise après la définition du module
2. Faire précéder le nom d'une fonction, d'un type ou d'un constructeur par le nom du module, par exemple on écrira `Usine.Lundi`.
Ajouter le code suivant à la fin de `usinelutins.ml`:
```ocaml
let usage () = print_endline "\nUsage: ocaml usinelutins.ml jour"
let run args =
match args with
| _pgm :: jour_s :: _ -> print_endline "Bonjour, nous sommes un certain jour"
| _ -> usage ()
let _ = run (Array.to_list Sys.argv)
```
> Modifier ce code afin afficher en plus `Nous sommes ` suivi d'un jour de la semaine traduit en string via un appel à `string_of_jour`.
> Vérifier le bon fonctionnement du programme en lançant `ocaml usinelutins.ml`. Modifier le code de façon à ce que le jour choisi soit obtenu à partir de `jour_s`. Gérer par un affichage approprié le cas ou `jour_s` n'est pas un jour.
### 4. Enrichir le module `Usine`
On veut maintenant définir au sein de l'usine de fabrication de jouets une notion de _configuration_ de la journée. La configuration d'une journée permet de connaître deux choses: pour chaque lutin, le jouet qu'il va fabriquer et pour chaque lutin et chaque jouet la quantité qu'il peut fabriquer. Les jouets et les lutins étant représentés par des chaînes de caractères, il se peut qu'un lutin ou un jouet passé en argument n'existe pas. On utilise donc une `option` pour gérer cette situation.
On va maintenant créer dans le module `Usine` un type `configuration` qui sera simplement une paire de fonctions. La première aura le type `string -> string option`, la seconde aura le type `string -> string -> int option`.
Définir ce type le module `Usine`:
```ocaml
type configuration = (string -> string option) * (string -> string -> int option);;
```
Ajouter les fonctions suivantes dans le module `Usine`:
- `mk_configuration: (string -> string option) -> (string -> string -> int option) -> configuration` cette fonction va fabriquer une paire avec ses deux arguments.
- `get_jouet: configuration -> (string -> string option)` cette fonction va extraire le premier élément de la paire
- `get_nb_jouets: configuration -> (string -> string -> int option)` cette fonction va extraire le deuxième élément de la paire
Tester ces fonctions avec des `assert` en utilisant une fonction qui renvoie toujours "toupie" pour le choix du jouet et toujours `42` pour le nombre de jouets.
### 5. Codage de l'application de gestion des jouets - début
On dispose à présent des éléments de langage pour pouvoir coder le début de l'application de gestion de l'usine de jouets.
Dans le module `Usine`, créer une configuration basée sur des `assoc` pour créer une variable globale pour y stocker, pour chaque jour, une configuration. Essayer de faire varier les fonctions de la configuration selon le jour.
Créer également une variable globale contenant la liste des noms des lutins.
Créer une fonction `calcule_jouets_config: configuration -> (string,int) list` qui indique pour chaque jouet combien d'exemplaires ont été fabriqué, en excluant les jouets fabriqués zéro fois.
Enfin créer un dernier module `LutinsApp` qui contiendra:
- une fonction `affiche_jouets: (string * int) list -> string` qui calculera une chaîne d'affichage de la sortie de la fonction `calcule_jouets_config`;
Modifier la fonction `run: string list -> unit` pour affichera (via `print_endline`) les jouets produits durant le jour passé en argument.
Avec l'implémentation actuelle, il est possible que certains jouets s'affichent plusieurs fois, cela sera corrigé avec la section suivante.
### 6. Optimisation du module Association
Changer le type et le code du module `Association` de façon à utiliser des arbres binaires de recherche à la place de listes de paires. Le code du reste de l'application ne devrait pas changer.
# Programmation fonctionnelle: TP8
## Séances précédentes
Terminer les TPs précédents.
## Calculatrice programmable
On prend comme point de départ la calculatrice du [TP3](tp3.md).
On veut maintenant ajouter dans la calculatrice la possibilité de coder des fonctions.
On ajoute deux constructeurs aux expressions:
- `Fun` pour représenter la création d'une fonction (ce qui correspond à un `fun x -> exp` en en OCaml et à λx.expr en λ-calcul);
- Ce constructeur aura deux données: le nom de la variable et l'expression correspondant au corps de la fonction.
- `App` pour représenter l'application d'une fonction
- Ce constructeur aura également deux données: l'expression qui donnera la fonction à appeler et l'expression qui donnera l'argument à passer à cette fonction.
On modifie également le type `resultat` car un résultat peut maintenant être une fonction:
- `Ok` devient `Int`
- On ajoute un constructeur `Fermeture` qui représente une fermeture, c'est à dire une fonction et sont environnement d'exécution
- Ce constructeur aura 3 données: le nom du paramètre, l'expression du corps de la fonction et enfin l'environnement de la fonction (représenté par une liste de paires `string * resultat`)
Il reste à mettre à jour la fonction d'évaluation des expressions.
Les environnements sont maintenant représentés, comme dans les fermetures, par les listes de paires `string * resultat`.
Il faut maintenant coder l'évaluation des nouvelles expressions.
L'évaluation d'un `Fun` consiste à créer la fermeture correspondante.
La variable et l'expression sont déjà fournies dans les données du `Fun`.
Pour l'environnement, on pourra dans un premier temps prendre tout l'environnement courant.
L'évaluation d'un `App` consiste à:
1. Évaluer l'expression qui calcule la fonction. Celle-ci doit renvoyer une fermeture (voir les erreurs plus loin)
2. Évaluer l'expression de l'argument.
3. Créer un nouvel environnement constitué de l'environnement de la fermeture auquel on ajoute la paire (x,v) où x est le nom du paramètre de la fermeture et v est la valeur calculée pour l'argument.
4. Évaluer le corps de la fonction dans ce nouvel environnement
**Erreurs:** si un appel récursif à une évaluation produit une erreur, on renverra cette erreur.
Si l'évaluation d'une expression calculant une fonction (_c.-à-d._ la première expression d'un `App`) produit un `Int`, on renverra une nouvelle erreur `PasUneFonction` (à ajouter au type `eval_err`).
Si une `Fermeture` est passée en argument à un opérateur binaire, renvoyer une nouvelle erreur `PasUnInt`.
**Pour aller plus loin:** maintenant que l'on dispose d'une construction pour appliquer des fonctions, les opérateurs binaires pourraient être recodés comme des des fonctions _natives_.
Une fonction native peut être représentée par une fonction OCaml qui prend un résultat en argument et renvoie un nouveau résultat.
Si on veut coder une fonction à deux arguments, il suffit de la curryfier, c'est-à-dire que le `resultat` renvoyé par la fonction OCaml associée est à nouveau une fonction native qui prendra le deuxième argument qui renverra le résultat final.
Ajouter un constructeur `Native` dans le type `resultat` et modifier la fonction d'évaluation en traitant le cas de `Native` similairement au cas `Fermeture`, sauf pour `App`.
Pour ce dernier cas, on évaluera simplement l'argument puis on appelera la fonction OCaml donnée dans `Native` pour obtenir le résultat.
Enfin on écrira des fonctions natives pour les 4 opérateurs binaires et on créera un environnement initial associant à chaque symbole binaire sa fonction native.
Dans les tests, on remplacera l'utilisation de Binop par des `App` avec comme fonction la variable ayant le symbole binaire concerné, par exemple: `(App(App(Var "+", Cst 2), Cst 3))` permet de représenter `2 + 3`.