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__filterpour 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-intsur 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 desetet 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__filterci-dessus en utilisant ces opérations. - Donner le code de ces opérations et la modification de
struct setdans 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.hpeut-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__filterajoute toujours en fin. Pour chaque mise en œuvre, quelle optimisation deset__addpeut être envisagée?
Abstraire le parcours avec un curseur externe
Commencer par créer une branche nommée
cursor-extsur 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 desetet 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 cursorainsi que les prototypes des opérations nécessaires dans le fichierset/set.h. - Quelles informations doit stocker
struct cursorpour chacune des mises en œuvre destruct set? - Définir la structure
struct cursorpour chacune des implémentations dans les fichierssentinel/set_sentinel.h,dynamic/set_dynamic.hetlink/set_link.h. - Mettre en œuvre les nouvelles fonctions déclarées dans le fichier
set/set.hpour chaque implémentation dans les fichierssentinel/set_sentinel.c,dynamic/set_dynamic.c,link/set_link.c. - Réécrire la fonction
set__filterci-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 curseurcde l’ensemblese. Votre implémentation doit s’assurer quecest 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émentxdans l’ensembleseà la position donnée par le curseurc. Cette fonction doit notamment vérifier que la positioncest 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__filteren temps linéaire?