La boucle en C++

Saturne
Modifié (July 2023) dans Informatique théorique
Bonjour
Pourriez-vous me confirmer qu'en C++, dans une boucle comme celle-ci : for(i = i0; i < n; i++) { ...body...}, si i0 change dans body, alors il change aussi dans le for ? (ce qui n'est pas le cas de tous les langages)
Je cherche l'indice du plus grand vecteur pour l'ordre lexicographique dans un vecteur de vecteurs. J'ai fait ce prog qui a l'air de marcher : 
int firstIndex(std::vector<std::vector<int>> expnts) {
  const int n = expnts.size();
  int i0 = 0;
  for(int i = i0; i < n-1; i++) {
    std::vector<int> vi = expnts[i];
    for(int j = i+1; j < n; j++) {
      std::vector<int> vj = expnts[j];
      bool vjmax = std::lexicographical_compare(
        std::begin(vi), std::end(vi), std::begin(vj), std::end(vj)
      );
      if(vjmax) {
        i0 = j;
        break;
      } else {
        return i0;
      }
    }
  }
  return i0;
}

Réponses

  • troisqua
    Modifié (July 2023)
    @Saturne : ta question porte sur les règles de portée des identifiants ("scope" en anglais) : https://en.cppreference.com/w/cpp/language/scope
    Et donc oui, ton i0 est modifiable par la boucle (pourquoi ne le serait-il pas ?)
  • Je ne sais pas car on peut toujours éviter ça donc personne de sérieux ne code comme cela. D'ailleurs, en C++, ce genre de boucle se code souvent directement avec des itérateurs (iterator en anglais). Comme ça, on n'a pas à se prendre la tête.
  • @Bibix : qu'est-ce qui n'est pas sérieux précisément dans cet algorithme ?
  • Chalk
    Modifié (July 2023)
    Je confirme @Saturne.
  • Bibix
    Modifié (July 2023)
    @troisqua Ben par exemple, pour cet algorithme précisément, la deuxième boucle ne sert à rien car on n'arrive jamais à $j = i+2$. Donc non seulement le programme ne fonctionne pas, mais en plus modifier i0 ne sert à rien, on ne fait que comparer $v_i$ à $v_{i+1}$ jusqu'à obtenir le premier $i0$ tel que $v_{i0} \geq v_{i0+1}$ qui est renvoyé. Donc on peut juste réduire toute cette boucle et mettre à la fin "return n" et dans la boucle "return i" ce qui dispense d'utiliser ce i0 qui ne sert à rien.
    Un codeur sérieux, il fait en sorte que son code soit lisible rapidement par un autre codeur sérieux. Là, ce n'est clairement pas le cas.
  • Calli
    Modifié (July 2023)
    Bonjour,
    Il me semble qu'en C++
    for (initialisation; condition; opération) {instructions}
    est équivalent à 
    initialisation;
    while (condition) {
         instructions;
         opération;
    }
    
    De plus, tu pourrais écrire for(int i = 0; i < n-1; i++) au lieu de for(int i = i0; i < n-1; i++) tout simplement, non ?

    Et Bibix a raison quand il dit que le programme ne fait pas ce qu'on lui demande.
  • @Bibix : oui mais là tu commentes ce qu'il veut faire (je n'ai absolument pas regardé). Moi je parlais d'initialiser son i0 et de le modifier au cours de la boucle et je croyais que tu disais que, précisément cela, n'était pas sérieux.
  • Mais mince, j'ai posté une réponse et elle n'est pas là...
  • J'ai vérifié et @Calli a raison, le i0 ne change pas.
  • Si ton i0 ne "change pas" ce n'est pas un problème de scope lié au langage mais à ce que fait ton algo. Tu peux vérifier cela en affichant la trace de j à l'intérieur de ta comparaison.
  • Médiat_Suprème
    Modifié (July 2023)
    Le code suivant ne modifie pas i0.
    i0 = 0;
    for(int i = i0; i < 10; i++) 
         i0 = 15;
    Il ne faut pas respirer la compote, ça fait tousser.

    J'affirme péremptoirement que toute affirmation péremptoire est fausse
  • Saturne
    Modifié (July 2023)
    Ce n'est toujours pas assez "sérieux" pour certains mais est-ce bon là ? Vérifié sur un exemple uniquement, mais je vais voir si mon gros code crashe ou pas.
    int firstIndex(std::vector<std::vector<signed int>> expnts) {
      const int n = expnts.size();
      int i = 0;
      while(i < n-1) {
        std::vector<signed int> vi = expnts[i];
        for(j = i + 1; j < n; j++) {
          std::vector<signed int> vj = expnts[j];
          bool vjmax = std::lexicographical_compare(
            std::begin(vi), std::end(vi), std::begin(vj), std::end(vj)
          );
          if(vjmax) {
            i = j - 1;
            break;
          } else if(j == n-1) {
            return i;
          }
        }
        i++;
      }
      return i;
    }
  • Pardon il manque un truc... je teste et je reviens.
  • Saturne
    Modifié (July 2023)
    J'ai corrigé dans le message précédent. Mon code crashe encore :neutral:
  • Non troisqua, Calli a raison. 
  • Bibix
    Modifié (July 2023)
    @troisqua Si tu relis mes messages, tu verras que j'ai écrit dans mon premier message que ce n'était pas sérieux car on pouvait toujours faire sans ça puis dans mon deuxième message qu'on pouvait faire la même chose sans $i0$ ce qui montre bien que ce n'est pas sérieux dans ce cas précisément. Tu as bien demandé de préciser pour l'algorithme du premier message de l'OP et je t'ai bien répondu. Si tu voulais des précisions en général, alors il fallait le dire plus tôt.
    Le nouvelle version est déjà meilleure. Pourquoi ? Parce-qu'il suffit de lire en une seule fois pour comprendre ce qu'il se passe et qu'on n'a aucun doute sur le fonctionnement (sans avoir à aller regarder dans la doc'). Du bon code, c'est pas seulement un code qui marche. C'est d'ailleurs précisément ce que je t'ai répondu.
  • Bon je vais faire un café. Merci en tout cas.
  • Calli
    Modifié (July 2023)
    J'ai du mal à comprendre la discussion, mais j'essaie de répondre.
    Saturne a dit :
    J'ai vérifié et @Calli a raison, le i0 ne change pas.
    Je n'ai pas dit ça.
    L'équivalence entre le for et le while que j'ai donnée était pour expliquer le fonctionnement du for. Et ça montre que i0 pourrait changer. Par exemple, dans le code de @Médiat_Suprème ci-dessous (avec correction des erreurs syntaxiques), le i0 change (contrairement à ce qu'il dit).
    int i0 = 0;
    for(int i = i0; i < 10; i++) {i0 = 15;}
    Ensuite je suis d'accord quand @troisqua dit ça :  
    troisqua a dit :
    Si ton i0 ne "change pas" ce n'est pas un problème de scope lié au langage mais à ce que fait ton algo.
  • Oui mais tu as raison sur l'équivalence entre le "for" et le truc que tu as écrit. Je n'ai pas vérifié sur mon code, je me suis renseigné sur SatckOverflow.
  • Médiat_Suprème
    Modifié (July 2023)
    @Calli, il n'y a pas d'erreur dans mon code, mon compilateur me l'aurait dit (par contre les balises "code" n'ont pas fonctionné), les accolades ne sont pas requises.

    Quant à l'exécution, je ne sais pas où j'ai regardé, car en l'exécutant à nouveau, je constate bien que i0=15.
    Il ne faut pas respirer la compote, ça fait tousser.

    J'affirme péremptoirement que toute affirmation péremptoire est fausse
  • @Calli Si tu regardes bien mon message initial, on peut dire que pour cet exemple, i0 ne change pas.
  • @Saturne : Oui dans ton premier code je suis d'accord ! Mais je ne l'avais pas dit dans mon premier message alors je ne comprenais pas pourquoi tu disais "Calli a raison, le i0 ne change pas". C'est tout.  :D

    @Médiat_Suprème : Oui ok j'ai juste rajouté le int de la première ligne et des accolades pour plus de clarté.
  • [Utilisateur supprimé]
    Modifié (July 2023)
    Personnellement, je ne suis pas certain de comprendre ce que tu souhaites réaliser. D'ailleurs ton bout de code est globalement incompréhensible (si j'écrivais ça, je me ferais virer sur le champ :D).
    Toutefois, à vu d'œil je te dirais que : il n'y a pas grand intérêt à utiliser while pour la boucle externe : for et while sont globalement équivalents. De plus, il existe depuis C++11 des moyens bien plus élégants et moins prises de tête pour itérer sur les vecteurs, à la manière de Java ou Python :
    for(auto v : vs) {
    équivalent à
    for(auto it=begin(vs); it!=end(vs); ++it) {
    ce qui rend d'ailleurs caduc ce genre de lignes
    for(j = i + 1; j < n; j++) {
          std::vector vj = expnts[j];
    dignes de personnes qui codent en C++ comme elles coderaient en C.
  • ... sauf que j'ai besoin de j  :D
  • ça y est mon code marche !!!  :D

    Maintenant reste à voir s'il déchire ou pas.
  • Merci @dp pour le for(auto v : vs) ! Je ne connaissais pas.
  • C'était un des meilleurs ajouts de C++11. Heureux de vous le partager !
  • Calli
    Modifié (July 2023)
    @dp, peux-tu me dire aussi si les codes suivants sont équivalents s'il te plaît ?
    for (map<typea,typeb>::iterator it=mymap.begin(); it!=mymap.end(); ++it) {
        ... j'utilise ici it->first et it->second ...}
    et
    for (auto& [a,b] : mymap) {... j'utilise ici a et b ...}
    Et idem avec const_iterator, cbegin, cend et auto const& ?
    J'ai aussi vu un auto&& sur le net, mais je ne comprends pas la différence avec auto&.
  • [Utilisateur supprimé]
    Modifié (July 2023)
    C'est globalement équivalent, oui. La seconde forme ayant été introduite comme étant un syntactic sugar pour la première. Pour s'en convaincre, on peut essayer sur des exemples.
    #include <iostream>
    #include <map>
    
    int main(int argc, char *argv[]) {
        std::map<std::string, int> map{{"Paris", 75}, {"Lyon", 69}, {"Clermont-Ferrand", 63}};
    
        for (std::map<std::string,int>::iterator it=map.begin(); it!=map.end(); ++it) {
            std::cout << it->first << ":" << it->second << std::endl;
        }
    
        for(auto &[key,value] : map) {
            std::cout << key << ":" << value << std::endl;
        }
    
        return 0;
    }
    
    Ce qui nous retourne
    Clermont-Ferrand:63
    Lyon:69
    Paris:75
    Clermont-Ferrand:63
    Lyon:69
    Paris:75
    On remarquera que C++ est gentil et effectue le tri automatiquement sur les keys.
    Pour ce qui est de la différence entre auto&& et auto&, je ne suis pas certain d'être capable de l'expliquer correctement, du coup je te renvoie vers ce lien stackoverflow qui, je trouve est très bien.
  • Formidable ! Merci.
Connectez-vous ou Inscrivez-vous pour répondre.