diff --git a/README.md b/README.md index c141b28bf35866ba9fe20f53b9f55bcd1abf4d57..de5d93d2df981993b91fadece3b8c5fc9785c2e4 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,13 @@ ## Semestre 2023 Printemps -| jour | heure | type | supports / remarques | -| ----- | ------------- | ---- | -------------------------------------------------------------------- | -| 16/01 | 8h | CM | [Diapositives](cm/lifpf-cm1.pdf) | -| | 9h45 | TD | [Sujet](td/lifpf-td1-enonce.pdf) <br> Groupes B et E à 11h30 | -| 23/01 | 8h | CM | [Diapositives](cm/lifpf-cm2.pdf), [Script démos](cm/cm2-demo.md) | -| | 9h45 ou 11h30 | TP |  [Sujet](tp/tp1.md) <br> Groupe de TP, horaire et salle sur [tomuss] | +| jour | heure | type | supports / remarques | +| ----- | ------------- | -------- | ------------------------------------------------------------------------- | +| 16/01 | 8h | CM | [Diapositives](cm/lifpf-cm1.pdf) | +| | 9h45 | TD | [Sujet](td/lifpf-td1-enonce.pdf) <br> Groupes B et E à 11h30 | +| 23/01 | 8h | CM | [Diapositives](cm/lifpf-cm2.pdf), [Script démos](cm/cm2-demo.md) | +| | 9h45 ou 11h30 | TP | [Sujet](tp/tp1.md) <br> Groupe de TP, horaire et salle sur [tomuss] | +| 30/01 | 8h | TD + QCM | [Sujet](td/lifpf-td2-enonce.pdf) <br> Groupes A et F en salle Nautibus C1 | +| | 9h45 ou 11h30 | TP | [Sujet](tp/tp2.md) <br> Groupe de TP, horaire et salle sur [tomuss] | [tomuss]: https://tomuss.univ-lyon1.fr diff --git a/td/lifpf-td2-enonce.pdf b/td/lifpf-td2-enonce.pdf new file mode 100644 index 0000000000000000000000000000000000000000..34bae1a7d46f62950bf361c654f75d47a0ececdd Binary files /dev/null and b/td/lifpf-td2-enonce.pdf differ diff --git a/tp/tp1.md b/tp/tp1.md index ad46fb3dd2bec6d19f636d1bba702b8bb869f88a..e57abda129d758684bf0c374cf403400a7de84e7 100644 --- a/tp/tp1.md +++ b/tp/tp1.md @@ -375,5 +375,3 @@ valeur de liste par cas avec un `match` et fabriquer une liste comme résultat. > complètes de la chanson _99 bottles of beer_. On séparera chaque paragraphe du > suivant avec "\n\n". Vérifier que la variable a la bonne valeur en l'affichant > avec `print_endline chanson_99_bottles;;` . - -## 6. Application calculatrice avec notation polonaise inversée diff --git a/tp/tp1.ml b/tp/tp1.ml new file mode 100644 index 0000000000000000000000000000000000000000..0c3d332dc983140b0acdb458d0bb5f2bf2a9d734 --- /dev/null +++ b/tp/tp1.ml @@ -0,0 +1,265 @@ +(**********************************************************************) +(* 1.1 *) +(3 * 2) + (5 / 2);; +3.2 +. (7.4 /. 2.0);; +"AC" ^ "/" ^ "DC";; +true || (false && false);; + +(* && est prioritaire sur || *) +(false && false) || true;; + +(* 3 < 2.0 provoque une erreur car 3 et 2.0 n'ont pas le même type *) + +(**********************************************************************) +(* 1.2 *) + +(* float_of_int 3.0 provoque une erreur car 3.0 n'est pas un int *) +float_of_int 3;; +int_of_float;; +int_of_float 3.5 + 2;; +float_of_int (int_of_float 3.6 + 2) +. 1.5;; +(not false) && false;; +2 < 3 && "b" < "ab";; + +(**********************************************************************) +(* 1.3 *) +let x1 = 3.5 +. float_of_int 2 in +x1 +. 3.0 +;; + +let x1 = 3.5 +. float_of_int 2 in +let x2 = x1 *. x1 in +x2 *. 2.0 +;; + +(**********************************************************************) +(* 1.4 *) + +(if true then 3 else 5) > 4 + +(**********************************************************************) +(* 2 *) +let f x = x + 1;; + +3 + f 3 + +let discriminant a b c = (b *. b) -. (4.0 *. a *. c) + +(* Le type affiche par l'interpréteur est float -> float -> float -> float *) + +(** +[discriminant a b c] calcule le discriminant d'un trinôme. +Cette fonction est utile pour trouver les racines d'un trinôme. +@param a le coefficient d'ordre 2 +@param b le coefficient d'ordre 1 +@param c le coefficient d'ordre 0 +@return le discriminant +*) +let discriminant (a : float) (b : float) (c : float) : float = + (b *. b) -. (4.0 *. a *. c) +;; + +discriminant;; +discriminant 2.0 8.0 8.0 + +(* Erreur de type sur l'utilisation de a dans la def suivante: *) +(* + let discriminant (a:int) (b:float) (c:float): float = + b *. b -. 4.0 *. a *. c;; +*) + +(**********************************************************************) + +(* 3.1 *) +type couleur = Rouge | Jaune | Bleu;; + +(* La valeur Rouge est bien définie et reconnue comme étant de type couleur *) +Rouge;; + +(* L'égalité est bien (pré)définie sur les couleurs *) +Rouge = Rouge;; +Rouge != Bleu;; + +(* De même il existe un ordre par défaut sur les couleurs *) +Bleu > Jaune + +type couleur = Rouge | Jaune | Bleu | Violet | Orange | Vert + +type couleur = + | Rouge + | Jaune + | Bleu + | Violet + | Orange + | Vert + | RJB of int * int * int +;; + +(* le type est "int * float" c'est-à -dire une paire d'int et de float *) +3, 5.6;; + +(* c'est le triplet de type int * int * int *) +3, 5, 6 + +(**********************************************************************) +(* 3.2 *) + +let nom_couleur c = + match c with + | Rouge -> "rouge" + | Jaune -> "jaune" + | Bleu -> "bleu" + | Vert -> "vert" + | Violet -> "violet" + | Orange -> "orange" + | _ -> "mélange" + +(* + let nom_couleur c = + match c with + | Rouge -> "rouge" + | Jaune -> "jaune" + | Bleu -> "bleu" + | Vert -> "vert" + | Violet -> "violet" + | Orange -> "orange";; + Erreur: this pattern-matching is not exhaustive. + Elle indique que le match ne couvre pas tous les cas possibles. +*) + +(* + let nom_couleur c = + match c with + | _ -> "mélange" + | Rouge -> "rouge" + | Jaune -> "jaune" + | Bleu -> "bleu" + | Vert -> "vert" + | Violet -> "violet" + | Orange -> "orange";; + Erreur: this pattern-matching is not exhaustive. + Elle indique que le match ne couvre pas tous les cas possibles. +*) + +(** +Génère le paragraphe pour [n] bouteilles de bières. +@param n le nombre de bouteilles de bièere sur le mur au début du paragraphe. +@return le paragraphe +*) +let paragraphe_bottles (n : int) : string = + match n with + | 0 -> + "No more bottles of beer on the wall, no more bottles of beer.\n" + ^ "Go to the store and buy some more, 99 bottles of beer on the wall." + | 1 -> + "1 bottle of beer on the wall, 1 bottle of beer.\n" + ^ "Take one down and pass it around, no more bottles of beer on the wall." + | 2 -> + "2 bottles of beer on the wall, 2 bottles of beer.\n" + ^ "Take one down and pass it around, 1 bottle of beer on the wall." + | k -> + string_of_int k ^ " bottles of beer on the wall, " ^ string_of_int k + ^ " bottles of beer.\n" ^ "Take one down and pass it around, " + ^ string_of_int (k - 1) + ^ " bottles of beer on the wall." +;; + +(* tests *) + +paragraphe_bottles 0;; +paragraphe_bottles 1;; +paragraphe_bottles 2;; +paragraphe_bottles 7 + +(**********************************************************************) +(* 4 *) + +(** +Renvoie la somme des n premiers entiers +@param n le nombre d'entiers à sommer +@return la somme +*) +let rec sum_n (n : int) : int = if n <= 0 then 0 else n + sum_n (n - 1) + +(** +[factorielle n] calcule la factorielle des [n] premiers entiers +@param n doit être supérieur ou égal à 1 +@return !n +*) +let rec factorielle n = if n <= 1 then 1 else n * factorielle (n - 1) + +(**********************************************************************) +(* 5 *) +(* Erreur car on essaie d'ajouter une string dans une int list + "2" :: 3 :: [];; +*) + +(** +Cette fonction calcule la longueur d'une liste de `string`. +@param l la liste dont on veut la longueur +@return la longueur de l +*) +let rec longueur (l : string list) : int = + match l with [] -> 0 | _ :: l2 -> 1 + longueur l2 + +(* La valeur de l'élément de list n'est pas utilisée dans le deuxième cas du match. + On a donc pas besoin de la recupérer dans une variable et on met _ pour plus de clarté. *) + +(** + Cette fonction calcule la somme des éléments de la liste + @param l la liste dont on veut additionner les éléments + @return la somme des éléments de la liste +*) +let rec sum_f (l : float list) : float = + match l with [] -> 0.0 | x :: l2 -> x +. sum_f l2 + +(** +Cette fonction concatène les éléments de la liste en les séparant par [sep]. +@param l la liste dont on veut concaténer les éléments +@param sep le séparateur +@return les éléments de la liste [l] concaténé et séparés les uns des autres par [sep] +*) +let rec join_s (l : string list) (sep : string) : string = + match l with + | [] -> "" + | s :: [] -> s + | s :: l2 -> + (* Plus général que le cas précédent car l2 n'est pas forcément vide. + Comme le match applique le premier cas dans l'ordre du programme, + Les listes à 1 élément ne passent pas dans le 3eme cas du match. *) + s ^ sep ^ join_s l2 sep +;; + +(* test *) +join_s [ "a"; "b"; "c" ] "|" + +(** +Cette fonction créée une liste des int de n à 0 inclus. +@param n l'int de début de liste +@return la liste des entiers décroissants +*) +let rec liste_n_0 (n : int) : int list = + if n <= 0 then [ 0 ] else n :: liste_n_0 (n - 1) +;; + +(* test *) +liste_n_0 12 + +(** +[bottles_of_list [n1; n2; ...]] crée la liste des paragraphes de la chanson 99 +bottles of beer pour n1 puis n2 ... bouteilles. +@param l la liste des nombres de bouteilles à transformer +@return la liste des paragraphes correspondant aux int de l +*) +let rec bottles_of_list (l : int list) : string list = + match l with + | [] -> [] + | n :: l2 -> paragraphe_bottles n :: bottles_of_list l2 + +let chanson_99_bottles = + let nums = liste_n_0 99 in + let paragraphes = bottles_of_list nums in + join_s paragraphes "\n\n" +;; + +print_endline chanson_99_bottles diff --git a/tp/tp2.md b/tp/tp2.md new file mode 100644 index 0000000000000000000000000000000000000000..a7ef1058401aa2d1c7216dee3e3a6b7ca503c920 --- /dev/null +++ b/tp/tp2.md @@ -0,0 +1,176 @@ +# LIFPF: TP2 récursion sur les listes + +Finir le [TP1][tp1.md] avant de commencer ce TP. + +Le reste du TP consiste à travailler la récursion sur les listes. + +Comme dans le TP précédent, on prendra soin: + +- de donner le type des arguments et le type de retour de chaque fonction; +- d'écrire le commentaire de description de la fonction **avant** d'écrire la fonction. + +_Remarque:_ ajouter un commentaire au-dessus de la fonction n'est pas la même chose que d'écrire le commentaire avant d'écrire la fonction. N'oubliez pas: vous écrivez avant tout ce commentaire pour vous aider à écrire la fonction et ensuite seulement pour la documenter. + +On prendra également soin d'écrire des tests pour chaque fonction en écrivant des expressions du type `ma_fonction valeur1 valeur2 = valeur attendue;;` et en vérifiant avec l'interpréteur que ces tests s'évaluent à `true`. + +## 1. Concaténations de listes + +Dans cette partie on souhaite implémenter des fonctions qui vont concaténer des listes. + +### 1.1 Concaténation de 2 listes + +Implémenter la fonction `concatene` qui prend deux `int list` et renvoie la liste constituée des éléments de la première suivis des éléments de la seconde. + +Quelques exemples de tests (à compléter): + +```ocaml +concatene [ 1; 2; 3 ] [ 4; 5; 6 ] = [ 1; 2; 3; 4; 5; 6 ];; +concatene [] [ 4; 5; 6 ] = [ 4; 5; 6 ];; +``` + +### 1.2 Concaténation (applatissement) d'une liste de liste + +Implémenter la fonction `applatit` qui prend une `(int list) list` et renvoie la liste constituée de la concaténation des éléments de cette liste de liste. Cette fonction utilisera la fonction `concatene` codée auparavant. + +Quelques exemples de tests (à compléter): + +```ocaml +applatit [ [ 1; 2 ]; [ 3; 4; 5 ]; []; [ 6 ] ] = [ 1; 2; 3; 4; 5; 6 ];; +applatit [ [ 1 ] ] = [ 1 ];; +``` + +Faire une deuxième version `applatit2` ayant la même spécification que `applatit` mais qui n'utilise pas `concatene`. +Tester cette fonction avec les mêmes tests que `applatit`. + +## 2. Retournement de liste + +On souhaite implémenter une fonction `renverse` qui renverse une liste. + +L'algorithme naïf pour faire une inversion de liste consiste à inverser la queue de liste et à concaténer la tête de liste à la fin de la queue renversée. Cet algorithme est cependant inefficace car il conduit à effectuer un nombre quadratique (en fonction de la taille de la liste initiale) d'ajouts en tête de liste à cause de l'opération de concaténation. + +On veut donc implémenter un algorithme plus efficace qui ne fait qu'un nombre linéaire d'ajout en tête de liste. +Pour cela on code auparavant une version modifiée `renverse_ajoute` qui prend deux listes en argument et renvoie la première renversée concaténée à la seconde, par exemple + +```ocaml +renverse_ajoute [ 1; 2; 3 ] [ 4; 5; 6 ] = [ 3; 2; 1; 4; 5; 6] +``` + +on peut remarquer les égalités suivantes sont vraies: + +``` +renverse_ajoute [ 1; 2; 3 ] [ 4; 5; 6 ] += renverse_ajoute [ 2; 3 ] [ 1; 4; 5; 6 ] += renverse_ajoute [ 3 ] [ 2; 1; 4; 5; 6 ] += renverse_ajoute [] [ 3; 2; 1; 4; 5; 6 ] +``` + +On peut aussi remarquer que `renverse` peut se coder très facilement à partir de `renverse_ajoute` puisque `renverse l = renverse_ajoute l []`. + +Coder `renverse` et définissant `renverse_ajoute`. +Définir tout d'abord `renverse_ajoute` avant `renverse` et tester. Essayer ensuite d'intégrer la définition de `renverse_ajoute` à l'intégrieur de celle de renverse via un `let rec renverse_ajoute ... in ...`. + +## 3. Tri par insersion + +On code l'algorithme de tri par insersion qui consiste, pour chaque élément, à l'insérer à la bonne place dans le reste de la liste préalablement trié. + +### 3.1 Insersion dans une liste triée + +On commence donc par coder une fonction `insersion` qui insère un élément dans une liste triée. + +Quelques tests à compléter: + +```ocaml +insertion 3 [ 1; 2; 4; 5 ] = [ 1; 2; 3; 4; 5 ];; +insertion 3 [ 1; 2; 3; 4; 5 ] = [ 1; 2; 3; 3; 4; 5 ];; +``` + +### 3.2 Tri + +Coder la fonction `tri_insersion` en utilisant `insersion`. + +Quelques tests à compléter: + +```ocaml +tri_insertion [ 1; 4; 2; 3 ] = [ 1; 2; 3; 4 ];; +tri_insertion [ 1; 2; 3; 4 ] = [ 1; 2; 3; 4 ];; +``` + +## 4. Recherche dans une liste d'association + +On rappelle qu'une liste d'association est une liste de paires (clé, valeur). L'opération principale sur ces listes consiste à chercher la valeur associée à une clé si elle existe. + +Dans cette question on considèrera que les clé sont de type `int` et les valeurs de type `string`. + +### 4.1 Type résultat + +Comme la clé n'est pas forcément présente dans la liste, on commence par créer un type somme `resultat` avec un constructeur représentant l'absence de valeur et un constructeur représentant le cas où une valeur a été trouvée. +Pour cela il faut essentiellement se poser la question des données éventuellement associées à chaque constructeur, ainsi que de leur type. + +### 4.2 Fonction de recherche + +Coder la fonction `cherche` qui va chercher une clé dans une liste d'association et renvoyer la valeur associée lorsqu'elle existe. + +Bien penser à écrire des tests pour cette fonction. + +## 5. Calculatrice en notation polonaise + +On souhaite implémenter une calculatrice en notation polonaise, c'est à dire calculant le résultat d'expressions dans laquelle les opérateurs sont placés avant leur arguments. +On donne quelques exemples dans le tableau suivant: + +| Notation habituelle | Notation polonaise | +| -------------------- | ------------------ | +| 3 \* 2 | \* 3 2 | +| 7 / 3 | / 7 3 | +| (7 / 3) + 5 | + / 7 3 5 | +| (3\*2) - ((7/3) + 5) | - \* 3 2 + / 7 3 5 | + +On peut remarquer que cette notation permet de se passer de parenthèses. + +Avant d'évaluer ce type d'expressions il faut pouvoir les représenter. On va donc se munir d'un type pour représenter les opérateurs, d'un type représentant les éléments d'expression (c'est-à -dire un opérateur ou un nombre): + +```ocaml +type binop = Plus | Moins | Mult | Div +type elt_expr = Op of binop | Cst of int +``` + +De plus l'évaluation d'une expression peut mal se passer pour deux raisons: une division par zéro ou une expression mal construite. +Par exemple `+ - 2 3` est mal construite car il manque un argument au `+`. +On va donc créer un type pour représenter un résultat ou une erreur: + +```ocaml +type resultat = Ok of int | ErrDivZero | ErrExpr +``` + +### 5.1 Évaluation des opérations + +Afin de simplifier le code de l'évaluateur, on commence par créer une fonction `eval_op` qui va évaluer le résultat d'un opérateur appliqué à des valeurs. +Comme il faut prendre en compte les erreurs de division par zéro, on renverra un `resultat` et pas un `int`. De plus comme cette fonction sera utilisée avec des arguments qui peuvent eux-même être obtenu via l'évaluation de d'autres expressions, on va prendre en argument des `resultat` et pas simplement des `int`. Si un résultat est une erreur, on renverra cette erreur au lieu d'effectuer le calcul. Ainsi le type de `eval_op` devra être `binop -> resultat -> resultat -> resultat`. + +Quelques tests à compléter: + +```ocaml +eval_op Plus (Ok 1) (Ok 2) = Ok 3;; +eval_op Moins (Ok 2) (Ok 3) = Ok (-1);; +eval_op Div (Ok 3) (Ok 0) = ErrDivZero;; +eval_op Div (Ok 5) ErrExpr = ErrExpr;; +``` + +### 5.2 Évaluation d'une suite d'expression + +Une suite d'expressions est représentée par une liste d'éléments d'expression. On peut remarquer qu'une telle liste peut contenir plusieurs expressions, par exemple `+ 3 5 - 2 7`, est représentée par `[ Op Plus; Cst 3; Cst 5; Op Moins; Cst 2; Cst 7 ]` et correspond aux deux expressions écrites habituellement `3+5` et `2-7`. + +On va coder une fonction `eval_expr` qui va évaluer une suite d'expressions et renvoyer une liste de résultats. Pour y arriver on peut faire les remarques suivantes: + +- Il faut toujours évaluer le reste d'une suite d'expressions non vide. +- L'évaluation d'une constante est cette constante mais sous forme de résultat +- L'évaluation d'un opérateur binaire nécessite de récupérer le résultat des deux expressions suivantes, donc d'avoir déjà fait l'appel récursif pour évaluer le reste des expressions. +- Si l'appel récursif produit une liste de résultats contenant zéro ou un élément, il n'est pas possible d'évaluer un opérateur binaire. C'est le cas où l'expression est mal construite. + +Quelques tests à compléter: + +```ocaml +eval_expr [ Cst 3 ] = [ Ok 3 ];; +eval_expr [ Op Mult; Cst 3; Cst 2 ] = [ Ok 6 ];; +eval_expr [ Op Plus; Cst 3; Cst 5; Op Moins; Cst 2; Cst 7 ] = [ Ok 8; Ok (-5) ];; +eval_expr [ Op Plus; Op Div; Cst 7; Cst 3 ] = [ ErrExpr ];; +```