Skip to content
Snippets Groups Projects
Commit db6cfe50 authored by COQUERY EMMANUEL's avatar COQUERY EMMANUEL
Browse files

TD2 + TP2

parent ddf57466
Branches main
No related tags found
No related merge requests found
...@@ -2,11 +2,13 @@ ...@@ -2,11 +2,13 @@
## Semestre 2023 Printemps ## Semestre 2023 Printemps
| jour | heure | type | supports / remarques | | jour | heure | type | supports / remarques |
| ----- | ------------- | ---- | -------------------------------------------------------------------- | | ----- | ------------- | -------- | ------------------------------------------------------------------------- |
| 16/01 | 8h | CM | [Diapositives](cm/lifpf-cm1.pdf) | | 16/01 | 8h | CM | [Diapositives](cm/lifpf-cm1.pdf) |
| | 9h45 | TD | [Sujet](td/lifpf-td1-enonce.pdf) <br> Groupes B et E à 11h30 | | | 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) | | 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] | | | 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 [tomuss]: https://tomuss.univ-lyon1.fr
File added
...@@ -375,5 +375,3 @@ valeur de liste par cas avec un `match` et fabriquer une liste comme résultat. ...@@ -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 > 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 > suivant avec "\n\n". Vérifier que la variable a la bonne valeur en l'affichant
> avec `print_endline chanson_99_bottles;;` . > avec `print_endline chanson_99_bottles;;` .
## 6. Application calculatrice avec notation polonaise inversée
tp/tp1.ml 0 → 100644
(**********************************************************************)
(* 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
tp/tp2.md 0 → 100644
# 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 ];;
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment