Laboratoire 06: Théorèmes et révisions
Preuves de théorèmes
Théorèmes sur append
- Définir la fonction
appendqui prend deux listes en argument et renvoie la concaténation de ces deux listes.
- Démontrer que la liste vide est l’élément neutre à gauche pour
append, c’est-à-direappend [] xs = xspour toutxs - Démontrer que la liste vide est l’élément neutre à droite pour
append, c’est-à-direappend xs [] = xspour toutxs - Définir la fonction
revqui inverse une liste. Par exemple,rev [1; 2; 3] = [3; 2; 1] - Démontrer que
rev (append xs ys) = append (rev ys) (rev xs)
Théorèmes sur filter
- Définir la fonction
filterqui prend un prédicat et une liste en argument. Elle renvoie la liste filtrée. - Démontrer que
filter (fun _ -> true) l = l - Définissez la fonction
combinequi renvoie la conjonction de deux prédicats, c’est-à-dire quecombine f grenvoie un prédicat qui est vrai pour une valeurxsif xetg xsont vraies. - Démontrer que
filter (combine f g) l = filter f (filter g l) - Démontrer que
filter f (append xs ys) = append (filter f xs) (filter f ys)
Théorèmes sur fold
- Définir les fonctions
fold_leftetfold_right. - Définir une fonction
sumqui fait la somme des éléments d’une liste (sans utiliserfold_leftoufold_right) - Démontrer que
fold_left (+) 0 xs = sum xs - Démontrer que
fold_right (+) 0 xs = sum xs - Démontrer que
fold_left f init (append xs ys) = fold_left f init xs ++ fold_left f init ys
Révisions pour l’intra
Shadowing
Soit le programme suivant. Quelle est la valeur de x à la ligne indiquée ?
let x = 1 in
let f x =
let x =
x + 2 (* quelle est la valeur de x à cette ligne ? *)
in
x in
f x
Renommer de façon consistante ce x et s’assurer que le programme préserve la même sémantique.
RLE
Le codage par plage (run length encoding) consiste à compresser des données en remplaçant chaque séquence d’éléments identiques par une seule instance de l’élément ainsi que son nombre de répétitions.
On souhaite avoir deux fonctions, compress et decompress qui permettent de faire du codage par plage.
Il est attendu qu’elles fonctionnent de la façon suivante :
# compress [1;1;1;2;2;4;4;4;2;1;3;3]
[(3,1);(2,2);(3,4);(1,2);(1,1);(2,3)]
# decompress [(1, true); (2, false); (3, true)]
[frue; false; false; true; true; true]
- Donner la signature de
compressetdecompress. - Définir
compressetdecompresssans utiliser de replis (fold). - Donner une trace de l’évaluation de
compress [1,1,1,2]. - Énoncer une propriété qui est vraie pour la paire de fonction
compressetdecompress, qui permet de s’assurer que notre codage par plage est correct. - Définir
compressetdecompressen utilisant des replis (fold).
find
Donner une signature appropriée ainsi qu’une définition de la fonction suivante :
find est une fonction qui prend en paramètre un prédicat et une liste. Elle retourne le premier élément de la liste qui satisfait au prédicat.
On ne veut pas qu’une erreur soit générée s’il n’y a pas d’élément dans la liste qui satisfait au prédicat.
filter_map
Définir la fonction suivante :
val filter_map : ('a -> 'b option) -> 'a list -> 'b list
Donner ensuite un exemple d’utilisation de cette fonction ainsi que le résultat de cet exemple.
Listes associatives
Une manière de représenter un dictionnaire de valeurs en OCaml est d’utiliser une liste associative, qui associe des clés de type 'a à des valeurs de type b :
type ('a, 'b) dict = ('a * 'b) list
- Définir la fonction
lookupqui cherche un élément dans un dictionnaire et retourne sa valeur.
val lookup : 'a -> ('a, 'b) dict -> 'b option
- Définir la fonction
extendqui ajoute un élément dans un dictionnaire.
val extend : 'a -> 'b -> ('a, 'b) dict -> ('a, 'b) dict
-
Donner le type et définir une fonction
dict_maxqui retourne la clé de l’élément dont la valeur est maximale dans un dictionnaire. -
Donner le type et définir une fonction
valuesqui retourne toutes les valeurs d’une liste associative -
Donner le type et définir une fonction
dict_sumqui calcule la somme des valeurs d’un dictionnaire.
iterate
Donner une signature appropriée ainsi qu’une définition de la fonction suivante :
iterate est une fonction qui prend en paramètre une fonction f, et une valeur initiale.
Elle retourne la liste infinie construite an appliquant f à la valeur initiale, ensuite à ce résultat, etc.
Par exemple, iterate (+1) 0 retourne la liste des entiers positifs.
Conversion
Définir la fonction suivante :
convert : (a -> c) -> (b -> c) -> (('a, 'b) result) list -> 'c list
Pour rappel, le type result est défini comme suit :
type ('a, 'b) result = Ok of 'a | Error of 'b
Donner ensuite un exemple d’utilisation de cette fonction ainsi que le résultat.
Définition de type
Définir un type pour représenter une valeur JSON. Une valeur JSON peut être :
- une chaîne de caractère (
"foo") - un nombre (
42) - une liste de valeurs JSON (
["foo", 42, 1, 3]) - un dictionnaire qui associe des clés (chaînes de caractères) à des valeurs JSON (
{"clé1": "valeur1", "clé2": 10})
Intra Été 2024
Question 1
Qu’est-ce qui est vrai pour les langages contenant des fonctions qui peuvent être de première classe et d’ordre supérieur ?
- On peut passer des fonctions en argument.
- Les fonctions sont des méthodes de classe.
- Les fonctions ne peuvent manipuler que des types primitifs (nombres, caractères, et chaînes de caractères).
- On peut créer des fonctions dans des fonctions pour les retourner.
Question 2
Les nombres de Stirling de second ordre sont définis par la formulation mathématique suivante : \begin{align*} S(n, 0) &= 0 \text{ pour } n > 0\newline S(0, k) &= 0 \text{ pour } k > 0\newline S(0, 0) &= 1 \newline S(n, k) &= k \times S(n-1, k) + S(n - 1, k - 1) \text{ pour } n > 0, k > 0 \end{align*}
Donner une définition de cette fonction en OCaml.
Question 3
Soit la définition suivante :
let f x y = x + (2 * y)
On souhaite la réécrire en utilisant la forme suivante. Écrire sa définition complète en remplaçant les ???.
let f = function ??? -> ???
Pour rappel, le mot clé function ne permet de définir une fonction qu’à un seul argument.
Question 4
Quel est la valeur qui résulte de l’évaluation de l’expression suivante ?
let f = fun x -> (fun y -> (fun x -> (x, y))) in
f 1 2 3
Question 5
Quel est la valeur qui résulte de l’évaluation de l’expression suivante ?
List.filter (fun x -> x > 5)
(List.map (min 7)
[1; 2; 3; 4; 5; 6; 7; 8; 9; 10])
Pour rappel, la fonction min peut être définie comme suit :
let min (x : int) (y : int) : int =
if x < y then
x
else
y
Question 6
Soit la fonction length définie comme suit :
let rec length (l : 'a list) : int =
match l with
| [] -> 0
| _ :: t -> 1 + (length t)
On observe le comportement suivant :
# length (List.init 500000 (fun _ -> 0));;
Stack overflow during evaluation (looping recursion?).
-
Expliquer quel est le problème.
-
Donner une trace des appels récursifs pour l’appel
length [1; 2; 3] -
Définir une version alternative de
lengthqui n’a pas ce problème, sans utiliser de fonction du moduleList.
Question 7
Donner le type et définir une fonction is_increasing qui vérifie si une liste est composé d’entiers strictement croissants. Vous pouvez utiliser des fonctions du module List, mais il est possible également de définir cette fonction sans en utiliser.
Question 8
On souhaite représenter un système de fichiers hiérarchique par un type algébrique en OCaml. Dans ce système de fichiers, on souhaite deux types d’entrées :
- des fichiers réguliers, ayant un contenu représenté par une chaîne de caractère.
- des dossiers, pouvant contenir d’autres fichiers (fichiers réguliers ou dossiers).
De plus, on souhaite attribuer des permissions à chaque fichier (régulier et dossier), parmi les permissions classiques : permission de lecture (read), d’écriture (write), et d’exécution (execute). Un fichier peut avoir plusieurs permissions à la fois (par exemple, être accessible en lecture et écriture en même temps).
Modéliser cela sous forme de types algébriques.
Question 9
Soit le type suivant :
type expr =
| Plus of expr * expr (* a + b *)
| Minus of expr * expr (* a - b *)
| Times of expr * expr (* a * b *)
| Divide of expr * expr (* a / b *)
| Num of int
Définir une fonction simplify qui remplace, pour toute expression x et pour toute constante n (introduite avec le constructeur Num) :
1 * xetx * 1par l’expressionx,0 + xetx + 0par l’expressionx,x / 1par l’expressionx0 - npar la constante-nx - 0par l’expressionx
Question 10
Donner le type et définir une fonction qui calcule le nombre de fois qu’un certain élément apparaît dans une liste. Donner deux définitions :
- une sans utiliser de repli
- une en utilisant un repli
Question 11
Plutôt que de représenter une liste comme son premier élément et un reste, on peut représenter une liste comme son préfixe et le dernier élément. C’est ce qu’on appellera une Tsil.
Soit la liste suivante :
1 :: (2 :: (3 :: []))
Son homologue Tsil pourrait être noté :
((([] :: 1) :: 2) :: 3)
Ou, avec des constructeurs nommés Nil et Snoc :
Snoc (Snoc (Snoc Nil 1) 2) 3
Soit la signature de module qui suit.
module type TSIL = sig
type 'a t = Nil | Snoc of 'a t * 'a
val nil : 'a t
val snoc : 'a t -> 'a -> 'a t
val length : 'a t -> int
val map : ('a -> 'b) -> 'a t -> 'b t
end
Définir une implémentation d’un module Tsil ayant cette signature.
Question 12
Soit la fonction suivante :
let rec exp (x : int) (n : int) : int =
if n = 0 then 1 else x * exp x (n - 1)
On souhaite démontrer que, pour tout \(n \geq 0, m \geq 0\) :
exp x (m + n) = exp x m * exp x n
La preuve se fait par induction sur m
- Quels sont les cas à prouver ?
- Quelle est l’hypothèse d’induction ?
- Effectuer la preuve.
Plus d’exercices
Pour se préparer à l’intra, il est recommandé de :
- relire les diapos, refaire les exemples et exercices des diapos
- faire les exercices de labos qui n’ont pas pu être fait pendant les labos
- regarder la documentation de la bibliothèque standard et réimplémenter des fonctions. En particulier, voir les modules suivants :
- faire les exercices présents dans le livre de référence OCaml Programming: Correct + Efficient + Beautiful