correction et conseil sur un petit exercice sur les iterateurs

L’auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Voilà, comme d’hab je progresse à mon rythme en suivant le cours de C++ de zeste de savoir (actuellement au chapitre sur les conteneurs Parti I)… Bref dans ce petit exercice, j’avais pour but de remplacer les espaces à l’intérieur d’une chaîne de caractères par un caractère quelconque saisie. J’aimerais avoir des retours sur ce code afin de m’améliorer encore plus, et dire si possible, si je peux ajouter certaines choses pour m’entraîner (ou bien des sites dans lesquels je puisse trouver des exos <avec correction> pour débutant). Pour info, j’ai emprunté la petite chaîne de caractères de ce tutoriel, n’y voyez pas noir ;) . Merci en avance…

Voici le code :

#include <iostream>
#include <string>
#include <algorithm>

int main ()
{
    std::string p { "Exemple illustrant le tutoriel C++ de zeste de savoir." };
    
    char espace { ' ' };
    std::cout << "Entre un caractere : ";
    char caractere { '_' };
    std::cin >> caractere;
    

    std::string::iterator it_debut_sequence { p.begin() };
    std::string::iterator it_fin_sequence { p.end() };

    for (std::string::iterator it { p.begin() }; it != it_fin_sequence; ++it)
    {
        std::string::iterator it_find { std::find(it_debut_sequence, it_fin_sequence, espace) };
        
        if (* it_find == espace)
        {
            * it_find = caractere;
            it_debut_sequence = it_find;
        }

        else
        {
            break;
        }
    }

    std::cout << p << std::endl;

    return 0;
}

Je ne sais pas si je peux sécuriser l’entré utilisateur dans ce cas de figure… Même si je rentre tout et n’importe quoi, un tronque s’effectue automatiquement sans que j’interviens, du coup ça m’arrange un peu.

Édité par amalure

+0 -0

Cette réponse a aidé l’auteur du sujet

Salut,

l’opérateur » sur le flux extrait le premier caractère, les autres sont donc toujours présent dans le buffer (le prochain appel de » extraira le suivant). Tu as tout de même un cas d’erreur à gérer : lorsqu’il n’y a rien. Tu dois donc tout de même sécuriser ta saisie. Aussi tu pourrais récupérer une string et vérifier qu’elle ne contienne qu’un caractère que tu utiliseras ensuite, et si ce n’est pas le cas, redemander la saisie à l’utilisateur car ce n’est probablement pas ce qu’il souhaitait faire (car ce n’est pas comme cela qu’on utilise ton logiciel).

Concernant la déclaration de variable :

  • tu n’en as pas besoin d’autant
  • tu peux utiliser l’inférence de type à travers le mot-clef auto qui te permet d’améliorer la lisibilité en évitant d’écrire std::string::iterator à chaque fois
  • pour les "variables" qui sont là juste pour avoir une valeur fixe que tu vas utiliser à plusieurs endroit, comme "espace", tu peux le déclarer constexpr pour indiquer au compilateur de quoi il s’agit. Il peut ainsi vérifier qu’elle n’est jamais modifiée, et n’a pas besoin de lui alloué de l’espace mémoire de la même façon.

Et enfin, concernant ta boucle, ton algorithme ressemble à :

Pour chacune des lettres composant ma phrase
  Je cherche un espace entre la dernière occurrence trouvée et la fin de la phrase
  Si j'en trouve je remplace l'occurrence suivante par ma lettre
  Sinon je ne traite pas le reste des lettres de ma phrase

Alors que je suppose que ce que tu souhaitais faire est plutôt :

Tant que je trouve un espace entre la dernière occurrence trouvé et la fin
  Je remplace cette occurrence par ma lettre

jadis @leroivi - all in all we’re just another brick in the wall

+1 -0

Cette réponse a aidé l’auteur du sujet

(Grillé sur certains points, mais je poste quand-même.)

Concernant directement le C++, je conseillerais d’utiliser auto dès qu’on définit une variable à partir de la valeur de retour d’une fonction (et aussi pour ne pas avoir à retaper plusieurs fois des types d’itérateurs qui sont souvent très verbeux), en l’occurrence :

auto it_debut_sequence = p.begin();

Concernant l’algorithme, bien qu’il fonctionne, je trouve un peu difficile de comprendre ce qu’il fait. À première vue, on voit une boucle qui fait passer un itérateur it sur chaque caractère, mais ensuite on se rend compte que it n’est jamais utilisé à l’intérieur de la boucle, donc on se questionne sur son intérêt. De plus, on remarque que ce qui met fin à la boucle, c’est plutôt le break. Finalement, on se rend compte que c’est la valeur de it_debut_sequence qui est modifiée à chaque boucle afin de progresser dans l’algorithme, ce qui parait un peu contre-intuitif.

Voilà les points que j’améliorerais pour un algorithme du même style :

  1. Définir clairement l’itérateur qui va évoluer durant l’algorithme. Choisissons simplement it ; pas besoin de s’ennuyer avec it_debut_sequence et it_fin_sequence.

  2. Éviter de sortir d’une boucle avec break, afin d’éviter la confusion (à moins que cela ne simplifie considérablement le code). Ici, la condition pour rester dans la boucle est « tant que l’itérateur n’est pas arrivé à la fin », ce qui se traduit en : while (it != p.end()).

  3. Incrémenter l’itérateur à la fin de la boucle. Cela n’a pas d’effet sur le résultat, mais actuellement, ton algorithme va chercher à partir du caractère remplacé, bien qu’il est évident que ce ne soit pas un espace. L’incrémentation va donc réduire le nombre total d’itérations.

Au final, le code pourrait ressembler à ça :

auto it = p.begin();

while (it != p.end())
{
    it = std::find(it, p.end(), espace);
    
    if (it != p.end())
    {
        *it = caractere;
        ++it;
    }
}

Note que j’aurais pu également écrire for (auto it = p.begin(); it != p.end(); ++it), cependant je préfère ne pas utiliser for lorsque l’itérateur est également modifié à l’intérieur de la boucle, encore une fois afin d’éviter la confusion.

Note également que, dans la mesure où tu te permets d’utiliser une fonction de la bibliothèque standard, l’intégralité de ton algorithme peut être remplacer par :

std::replace(p.begin(), p.end(), espace, caractere);
+1 -0

Salut je viens de débuter aussi neanmoins j’ai déjà fini de lire ce cours. Je me demandais juste si l’on aurait pas fait simple comme ceci:

#include <iostream> #include <string> #include <algorithm> #include <ctype>

int main () { std::string p { "Exemple illustrant le tutoriel C++ de zeste de savoir." };

std::cout << "Entre un caractere : ";
char caractere { '_' };
secured_input(caractere) //En admettant qu'on l'ait implementé

std::for_each(std::begin(p),std::end(p),[caractere](auto &element)->{
if(isspace(element))
   element=caractere;
});

std::cout<<p<<std::endl;
return 0;

}

Je l’ai pas testé ….

+0 -0

Je me demandais juste si l’on aurait pas fait simple

magic|boy

Le plus simple, c’est la fonction replace comme suggère olybri.

Sinon, vos programme n’ont pas un comportement identique, car tu utilises la fonction (std::)isspace qui identifie les caractères qui ont une sémentique de séparation (d’après la doc ' ', '\f', '\r', '\n', '\t' et '\v') alors qu’amalure ne remplace que les caractères ' '. Voir la spec (la consigne) pour savoir ce qu’il faut vraiment faire.

Aussi, remarque que tu peux utiliser le for-range pour parcourir les conteneurs en entier, ça t’évites de faire des lambdas

et la syntaxe de la lambda c’est [capture](arguments)->type de retour{corps de fonction}

jadis @leroivi - all in all we’re just another brick in the wall

+1 -0

Je me demandais juste si l’on aurait pas fait simple

magic|boy

Le plus simple, c’est la fonction replace comme suggère olybri.

Sinon, vos programme n’ont pas un comportement identique, car tu utilises la fonction (std::)isspace qui identifie les caractères qui ont une sémentique de séparation (d’après la doc ' ', '\f', '\r', '\n', '\t' et '\v') alors qu’amalure ne remplace que les caractères ' '. Voir la spec (la consigne) pour savoir ce qu’il faut vraiment faire.

Aussi, remarque que tu peux utiliser le for-range pour parcourir les conteneurs en entier, ça t’évites de faire des lambdas

et la syntaxe de la lambda c’est [capture](arguments)->type de retour{corps de fonction}

romantik

Oui je vois , merci. J’ai omis le type de retour.

+0 -0
Auteur du sujet

Bonjour j’ai décidé de revoir certains acquis car j’avais vu que certaines choses que je croyais digérer ne les sont pas ou les sont moins que je les pensais. Donc je prendrai un peu de temps avant de poster un autre message ici (Merci bcp pour ces retours)

Bref je n’étais pas au courant de l’existence de la fonction std::replace()

J’ai appris que std::find() renvoie un itérateur sur le premier élément correspondant à la recherche. Du coup je voulais qu’elle renvoie un itérateur sur le deuxième élément correspondant a la recherche et ainsi de suite, et obtenir le tout par l’intermédiaire d’une boucle.

Et c’est à partir de là qu’il m’est venu l’idée de remplacer la valeur de chaque itérateur que cette fonction renvoie par un underscore, puis de laisser le choix à l’utilisateur. Pour être concis je voulais m’entraîner. Alors qu’avec la fonction std::replace() je ne m’exerce point comme je le fais… En tout cas ça m’a bcp aidé de savoir qu’elle existe. Je prends vos conseils et je l’appliquerai… bref je reposterai bientôt

+0 -0

Bonjour j’ai décidé de revoir certains acquis car j’avais vu que certaines choses que je croyais digérer ne les sont pas ou les sont moins que je les pensais. Donc je prendrai un peu de temps avant de poster un autre message ici (Merci bcp pour ces retours)

amalure

Aucun soucis, n’hésite surtout pas à poster, c’est pas grave de faire ces erreurs, c’est plus grave de ne pas les voir, et ça on peut t’y aider bien plus efficacement que ce que tu peux t’en rendre compte seul.

Bref je n’étais pas au courant de l’existence de la fonction std::replace()

amalure

On peut pas tout savoir, essaie de repérer les problématiques très commune et prends l’habitude de rechercher ce qui existe déjà pour y répondre.
Prends aussi le temps de parcourir la doc du standard dans une vue d’ensemble, ça te permettra d’avoir une idée de ce qu’il y a à disposition pour y revenir dessus en détail quand tu en auras besoin.

J’ai appris que std::find() renvoie un itérateur sur le premier élément correspondant à la recherche. Du coup je voulais qu’elle renvoie un itérateur sur le deuxième élément correspondant a la recherche et ainsi de suite, et obtenir le tout par l’intermédiaire d’une boucle.

amalure

Et c’est bien ce que tu fais, c’est juste que le choix de ta boucle n’est pas adapté donc ça la rend compliquée à relire (et entraine quelques petites allocations supplémentaires mais ça limite on s’en fous).

Pour être concis je voulais m’entraîner. Alors qu’avec la fonction std::replace() je ne m’exerce point comme je le fais…

amalure

Oui c’est pour ça qu’elle n’apparaît même pas dans mon post et qu’elle arrive qu’en petit à la fin de celui d’Olybri en mention "pour info". On a bien compris que le but est de t’entrainer sur les structures de contrôle. ^^

Bref je reposterai bientôt

amalure

au plaisir

Édité par romantik

jadis @leroivi - all in all we’re just another brick in the wall

+1 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte