Abstraction du parcours avec un curseur
En comparant le code de la fonction set__filter
dans les trois réalisations, vous pouvez remarquer que l’algorithme est très similaire. Nous cherchons à écrire cet algorithme une unique fois pour les trois réalisations.
Mise en œuvre par une opération externe
En utilisant les opérations définies dans set/set.h
, il est possible de réaliser une seule implémentation de set__filter
qui fonctionne pour toutes les réalisations:
struct set * set__filter(struct set const * se, int (*filter)(void*))
{
struct set * filtered = set__empty();
for (int i = 0; i < INT_MAX; ++i)
if (filter(i) && set__find(se, i))
set__add(filtered, i);
return filtered;
}
- Quelle est la complexité de la fonction
set__filter
pour chacune des réalisations? Comparer aux complexités des fonctions internes que vous avez implémentées précédemment.
Abstraire le parcours avec un curseur interne
Commencer par créer une branche nommée
cursor-int
sur votre dépôtgit
à partir de la branche principale. Pour la suite de cet exercice, vous travaillerez dans la branchecursor-int
. Nous allons modifier l’API deset
et la rendre incompatible avec d’autres exercices. Vous pourrez ainsi revenir à la branche principale au besoin.
Pour obtenir une meilleure complexité, nous avons besoin d’une abstraction du parcours d’un ensemble. Il faut néanmoins cacher certains détails de réalisation propres à chacune des mises en œuvre. Pour cela nous allons ajouter un curseur interne à nos ensembles struct set
.
- Quelles sont les quatre opérations à introduire pour manipuler ce curseur sans connaître la mise en œuvre de
struct set
? - Donner le prototype de ces opérations en considérant que le curseur est stocké dans
struct set
. - Réécrire le code de la fonction
set__filter
ci-dessus en utilisant ces opérations. - Donner le code de ces opérations et la modification de
struct set
dans les trois réalisations. - Quelles sont les opérations de l’ensemble qui peuvent rendre le curseur incohérent?
- Quelle(s) autre(s) opération(s) définie(s) dans le fichier
set/set.h
peut-on écrire en utilisant le curseur, tout en préservant la complexité? - Quelle est la limite principale due au stockage du curseur dans la structure
struct set
? - On remarque que
set__filter
ajoute toujours en fin. Pour chaque mise en œuvre, quelle optimisation deset__add
peut être envisagée?
Abstraire le parcours avec un curseur externe
Commencer par créer une branche nommée
cursor-ext
sur votre dépôtgit
à partir de la branche principale. Pour la suite de cet exercice, vous travaillerez dans la branchecursor-ext
. Nous allons modifier l’API deset
et la rendre incompatible avec d’autres exercices. Vous pourrez ainsi revenir à la branche principale au besoin.
Certains traitements nécessitent de réaliser plusieurs parcours simultanés d’une structure de données. Le curseur interne ne le permet pas. Dans cette partie, nous souhaitons donc mettre en œuvre un curseur externe qui est stocké dans une structure dédiée struct cursor
. Chaque mise en œuvre de struct set
proposera sa propre réalisation de struct cursor
. Cette structure sera abstraite: l’utilisateur ne manipulera que des pointeurs struct cursor *
.
- Ajouter la déclaration du type
struct cursor
ainsi que les prototypes des opérations nécessaires dans le fichierset/set.h
. - Quelles informations doit stocker
struct cursor
pour chacune des mises en œuvre destruct set
? - Définir la structure
struct cursor
pour chacune des implémentations dans les fichierssentinel/set_sentinel.h
,dynamic/set_dynamic.h
etlink/set_link.h
. - Mettre en œuvre les nouvelles fonctions déclarées dans le fichier
set/set.h
pour chaque implémentation dans les fichierssentinel/set_sentinel.c
,dynamic/set_dynamic.c
,link/set_link.c
. - Réécrire la fonction
set__filter
ci-dessus en utilisant un curseur externe. Adapter vos tests fonctionnels et assurez-vous que tous vos tests soient bien validés. - Les opérations
set__add
,set__remove
, etc., peuvent invalider les curseurs. Quel mécanisme pouvez-vous mettre en œuvre pour que l’utilisation d’un curseur invalide puisse être détectée? - Ajouter la fonction
int set__remove_cursor(struct set * se, struct cursor * c);
qui retire l’élément pointé par le curseurc
de l’ensemblese
. Votre implémentation doit s’assurer quec
est bien un curseur valide surse
, et que le curseur reste valide après la suppression. - On souhaite ajouter à une position donnée dans un
struct set
à l’aide d’un curseur via la fonctionint set__add_cursor(struct set * se, struct cursor * c, void* x);
. Cette fonction ajoute l’élémentx
dans l’ensemblese
à la position donnée par le curseurc
. Cette fonction doit notamment vérifier que la positionc
est valide. Comment implémenter cette vérification en temps constant dans chaque mise en œuvre destruct set
? Que peut-on en déduire sur la mise en œuvre deset__filter
en temps linéaire?