diff --git a/README.md b/README.md index e3b74d573ab6fa1592e026b548d655fa9c72e055..cc06af9cd4aca88ea8bdf4e91bfafff6520b8ce8 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ | 27/03 | 9h45 ou 11h15 | TP | [Sujet](tp/tp7.md) | | 03/04 | 8h | CM | [Diapositives](cm/lifpf-cm6.pdf), [code](cm/cm6-code.ml) | | | 9h45 | TD + Contrôle | [Sujet](td/lifpf-td6-enonce.pdf) | +| 24/04 | 9h45 ou 11h15 | TP | [Sujet](tp/tp8.md) <br> Horaires et groupes sur [tomuss] | ###### Évaluation diff --git a/tp/tp8.md b/tp/tp8.md new file mode 100644 index 0000000000000000000000000000000000000000..ad5d9efeff1d10a7fc266f9d8918b2fb98575ef2 --- /dev/null +++ b/tp/tp8.md @@ -0,0 +1,53 @@ +# 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`.