Mein Informatiklehrer sagt, dass



  • Erhard Henkes schrieb:

    OK, nehmen wir ein gestelltes Schul-Beispiel:

    #include <iostream>
    using namespace std;
    
    int main()
    {
      for(int i=0; i <= 100; ++i)
      {
        if(i%2)
          continue;
        else
          cout << i << ' ';
      }
    
      cout << endl << endl;
    
      for(int i=0; i <= 100; ++i)
      {
        if(i%2)
          cout << i << ' ';
        else
          continue;
      } 
    }
    

    Wie müsste das alles optimal ohne continue aussehen? ... und was ist dann der konkrete Vorteil? 🙂

    Äh, wenn ich über die geraden Zahlen iterieren will schreib ich

    for(int i=0; i<=100; i+=2)
    {
    }
    

    und bei ungeraden halt

    for(int i=1; i<=100; i+=2)
    

    Dass das geschickter ist brauchen wir wohl nicht wirklich zu diskutieren, oder? Das ist imo ein typischer Fall von dem von Simon2 erwähnten "über die genaue Bedingung mach ich mir später noch Gedanken".



  • Erhard Henkes schrieb:

    OK, nehmen wir ein gestelltes Schul-Beispiel:

    for(int i=0; i <= 100; ++i)
      {
        if(i%2)
          continue;
        else
          cout << i << ' ';
      }
    

    Wie müsste das alles optimal ohne continue aussehen? ... und was ist dann der konkrete Vorteil? 🙂

    Naja, was ist Dir lieber?

    for(int i=0; i <= 100; ++i)
      {
        if(istTeilbar(i,2))
          continue;
        else
          cout << i << ' ';
      }
    

    oder

    for(int i=0; i <= 100; ++i)
      {
        if(not istTeilbar(i,2))
          cout << i << ' ';
      }
    


  • Hi,

    also ich bin skeptisch, dass es ein "gutes Beispiel" für break/continue in Schleifen gibt, das gleichzeitig auch "klein" ist.
    Da, wo ich wirklich die beiden wirklich gewinnbringend (oder "kleinstes Übel" 😉 ) gesehen habe, waren es immer sehr kompliziert verschachtelte Schleifen/Bedingungen, bei denen nur in ganz seltenen Spezialfällen größere Blöcke übersprungen werden sollten.
    Eben Dinge, die sich nicht so einfach in einen if- oder else-Zweig packen lassen...

    Gruß,

    Simon2.



  • seien wir froh, dass überhaupt noch c++ irgendwo unterrichtet wird, die tendenz ist eine andere...



  • Ich stelle hier fest, das mehrheitlich dem Lehrer zugestimmt wird!

    Meine pers. Erfahrung ist die, das ich außer in switch-case fast nie break einsetze, wenn dann nur, weil ich mir nicht viel Gedanken für eine Alternative gemacht habe! continue habe ich glaube ich in meinem Leben nur einmal benutzt... zum Ausprobieren. 😃



  • Also meiner Ansicht nach hat der Lehrer im Prinzip recht. Häufig deuten break oder continue auf schlechten Code hin. Aus pädagogischer Sicht ist ein solcher Hinweis daher angebracht.

    Es gibt dennoch Situationen, in denen diese Konstrukte sinnvoll sind.

    Ein sehr einfaches Beispiel wäre:

    int z;
    while (true)  // ist das unsauber?
    {
      std::cout << "Bitte geben Sie eine Zahl zwischen 1 und 10 ein:";
      std::cin >> z;
      if (z >= 1 && z <= 10)
        break;
    
      std::cout << "Die Zahl " << z << " ist nicht zwischen 1 und 10.\n";
    }
    

    Ohne break hätte man ein Problem. Da müsste man die Bedingung 2 mal prüfen, was unschön ist:

    int z;
    do
    {
      std::cout << "Bitte geben Sie eine Zahl zwischen 1 und 10 ein:";
      std::cin >> z;
      if (z < 1 || z > 10)
        std::cout << "Die Zahl " << z << " ist nicht zwischen 1 und 10.\n";
    
    } while (z < 1 || z > 10); // 2. Prüfung
    

  • Administrator

    @tntnet,

    char const* const message = "Bitte geben Sie eine Zahl zwischen 1 und 10 ein:";
    std::cout << message;
    
    int z = 0;
    
    while(!(std::cin >> z) || z < 1 || z > 10)
    {
      std::cout << "Die Zahl " << z << " ist nicht zwischen 1 und 10.\n";
      std::cout << message;
    
      std::cin.clear();
      std::cin.ignore(std::cin.rdbuf()->in_avail());
    }
    

    Man kann sehr vieles umformen. Ich persönlich brauche continue fast nie, break ein wenig öfter, aber meistens schreibe ich Schleifen ohne break und continue .

    break und continue allerdings einfach zu verfluchen und verbieten, wie es ca. der Lehrer gemacht hat, würde ich nicht. Zum Teil gibt es Schleifen, wenn man dort kein break einführen würde, dann würde die Schleifen-Bedingung viel zu komplex und unübersichtlich werden oder die Schleife weisst dann x-fache if -Verschachtelungen auf. Es gilt halt wie für alle Sprachmittel: "Überlege bevor du sie verwendest".

    Grüssli



  • Ich stelle fest, dass Gegner von break und continue in diesem Thread keine Argumente bringen, sondern nur von ihrem ästetischen Empfinden berichten und/oder behaupten, es gäbe immer eine elegantere Schleifenvariante, die ohne break und continue auskommt. Letzteres ist natürlich schwer zu beweisen -- gerade auch weil Dijkstra und Knuth sich mit dem Thema auseinander gesetzt haben und zu dem Schluss gekommen sind, dass man mit goto s -- damals gabs noch kein break und continue -- in manchen Fällen Code-Duplikationen und "Sonderbedingungen" reduzieren kann. Ich denke, heutzutage bekommt man Dijkstras und Knuths Beispiele auch elegant ohne goto hin, wenn man break , continue und return benutzen darf.

    Gruß,
    SP



  • int z;
    std::cout << "Bitte geben Sie eine Zahl zwischen 1 und 10 ein:";
    std::cin >> z;
    while (z < 1 || z > 10) {
      std::cout << "Die Zahl " << z << " ist nicht zwischen 1 und 10.\nBitte geben Sie eine Zahl zwischen 1 und 10 ein:";
      std::cin >> z;
    }
    

    Ginge auch - da hätte man allerdings Ausgabe/Eingabe doppelt. Andererseits ist hier eine klare "Fehlerfallbehandlung" separat gekapselt, was IMHO auch Vorteile hat.

    Gruß,

    Simon2.



  • Um ehrlich zu sein, das würde ich so schreiben:

    int z;
    for (;;) {
      std::cout << "Bitte geben Sie eine Zahl zwischen 1 und 10 ein:";
      std::cin >> z;
      if (1 <= z && z <= 10) break;
      std::cout << "Die Zahl " << z << " ist nicht zwischen 1 und 10.\n";
    }
    

    Das ist ein typischer Kandidat für die "and-a-half"-Schleifen, bei denen die Abbruchbedingung weder am Anfang noch am Ende steht.

    Gruß,
    SP



  • Hallo,
    bei mir wird gar nicht mehr C++ unterrichtet. Ich lerne Delphi in der Schule aber da meine "Hauptsprache" nun mal
    C++ ist unterhalte ich mich mit meinem Lehrer schon mal darüber! Als ich ihm ein kleines Programm gezeigt habe, meinte er halt, dass
    break und continue schlechter Stil wäre und dass man sie meiden solle...

    Ich war damals noch nicht so erfahren und habe einfach gesagt "Ich weiß!" doch darüber schäme ich mich, denn
    ich sagte es nur aus Unwissenheit bzw und meinen Lehrer zufrieden zu stellen!

    Heute würde mir das nicht mehr passieren, da ich der Meinung bin, dass break und continue durchaus angebracht sind!

    Vielen Dank
    lg, freakC++



  • Es kommt halt immer auf die Situation an. Auch ein goto hat seine Berechtigung. Wenn man allerdings goto , oder auch break und continue ständig braucht lediglich, weil man zu faul ist sich, wie schon vorhin gezeigt, gedanken über Bedingungen o.ä. zu machen, dann ist das bestimmt schlechter Stil.
    Wen man allerdings einen Grund hat und Alternativen zu umständlich/unleserlich wären, dann kann man alles brauchen, was die Sprache hergibt.



  • Ich stelle fest, dass Gegner von break und continue in diesem Thread keine Argumente bringen

    Vollkommen richtig, ich dachte die Argumente wären allen bekannt 🙄

    Wie auch immer break, continue und return sind ebenso Sprunganweisungen wie goto, es gilt die selbe Argumentation...
    Man kann also nur diskutieren ob die Probleme, die durch Sprunganweisungen verursacht werden nicht geringer sind als die, die durch die umstrukturierung verursacht werden, deswegen gibt es nur Beispiele, wie man es besser machen kann.

    deswegen =>

    Als ich ihm ein kleines Programm gezeigt habe, meinte er halt, dass
    break und continue schlechter Stil wäre und dass man sie meiden solle...

    So formuliert imho richtig. Meiden heißt aber auch nicht, gar nicht verwenden. Wer weiß was du ihm vorgelegt hast 😃



  • JustAnotherNoob schrieb:

    Wie auch immer break, continue und return sind ebenso Sprunganweisungen wie goto, es gilt die selbe Argumentation...

    Nicht zu vergessen for, while, if/else, case



  • Gerade auf meiner Platte gefunden:
    C:\angstkopie\drvd\src\raetsel\main.cpp

    #include <iostream>
    
    using namespace std;
    
    bool istPrim(int kandidat)
    {
       bool prim=true;
    
       for(int i=2;i<=kandidat-1;i=i+1)
       {
          if(kandidat%i == 0)
          {
             prim=false;
             break;
          }
       }
    
       return prim;
    }
    
    int main()
    {
        for(int a=0;a<=9;++a)
        {
            for(int b=0;b<=9;++b)
            {
                for(int c=0;c<=9;++c)
                {
                    for(int d=0;d<=9;++d)
                    {
                        if(a+b+c+d!=26)
                            continue;
                        if((a*b*c*d)%2!=0)
                            continue;
                        if(c!=d)
                            continue;
                        if(!istPrim(100*b+10*c+d))
                            continue;
                        cout<<a<<b<<c<<d<<'\n';
                    }
                }
            }
        }
        return 0;
    }
    

    edit: das rätsel dazu war anscheinend
    http://www.bu-on.de/raetsel/raetsel2.htm

    Die Ausgabe des Programms ist

    4499
    4877
    6677
    

    Die Bedingung 3) wollte ich nicht ausprogrammieren.

    Übrigens ist die istPrim schrecklich mit der komischen Variablen da. Warum nicht gleich rausreturnen, wenn sie nicht prim ist?



  • JustAnotherNoob schrieb:

    ich dachte die Argumente wären allen bekannt 🙄

    Welche sind denn das? Bring das doch mal auf'n Punkt!

    Gesprungen wird immer irgendwo irgendwie, zB auch beim Funktionsaufruf. :p

    Das wichtige dabei ist doch die Lesbarkeit. Wenn ich ein "goto" sehe, dann muss ich erstmal die Sprungmarke suchen, um zu wissen, wo's weiter geht. Das ist schlechte daran. Es hat auch zur Folge, dass es schwieriger wird, sich von der Korrektheit des Programms zu überzeugen.

    Wie sieht es mit break, continue und return aus? Wenn Deine Schleifen mehrere Seiten lang sind, hast Du sowieso etwas falsch gemacht. Bei break und continue ist sofort klar, wo's weitergeht. Mit return springe ich aus der Funktion heraus. Willst Du mir jetzt erzählen dass die Nutzung von break, continue und return die Lesbarkeit verschlechtert? Wie würdest Du denn das Beispiel von tntnet mit der "and-a-half"-Schleife umsetzen?

    So vielleicht? (Mit Zusatzvariable)

    bool wiederholen = true;
      do {
        AAA
        wiederholen = ....; // Test
        if (wiederholen) {
          BBB
        }
      } while (wiederholen);
    

    Oder so vielleicht? (Mit Code Duplication)

    AAA
      while (...) {
        BBB
        AAA
      }
    

    ?

    Ist doch alles Käse!

    Gruß,
    SP



  • #include <iostream> 
    
    using namespace std; 
    
    bool istPrim(int kandidat) 
    { 
       bool prim=true; 
    
       for(int i=2;i<=kandidat-1;i=i+1) 
       { 
          if(kandidat%i == 0) 
          { 
             prim=false; 
             return prim; 
          } 
       } 
       return prim; 
    } 
    
    int main() 
    { 
        for(int a=0;a<=9;++a) 
        { 
            for(int b=0;b<=9;++b) 
            { 
                for(int c=0;c<=9;++c) 
                { 
                    for(int d=0;d<=9;++d) 
                    { 
                        if(a+b+c+d!=26)
                            ;
                        else if((a*b*c*d)%2!=0) 
                            ; 
                        else if(c!=d) 
                            ; 
                        else if(!istPrim(100*b+10*c+d)) 
                            ; 
                        else
                            cout<<a<<b<<c<<d<<'\n'; 
                    } 
                } 
            } 
        } 
        return 0; 
    }
    


  • adfkhl schrieb:

    #include <iostream> 
    
    using namespace std; 
    
    bool istPrim(int kandidat) 
    { 
       bool prim=true; 
    
       for(int i=2;i<=kandidat-1;i=i+1) 
       { 
          if(kandidat%i == 0) 
          { 
             prim=false; 
             return prim; 
          } 
       } 
       return prim; 
    } 
    
    int main() 
    { 
        for(int a=0;a<=9;++a) 
        { 
            for(int b=0;b<=9;++b) 
            { 
                for(int c=0;c<=9;++c) 
                { 
                    for(int d=0;d<=9;++d) 
                    { 
                        if(a+b+c+d!=26)
                            ;
                        else if((a*b*c*d)%2!=0) 
                            ; 
                        else if(c!=d) 
                            ; 
                        else if(!istPrim(100*b+10*c+d)) 
                            ; 
                        else
                            cout<<a<<b<<c<<d<<'\n'; 
                    } 
                } 
            } 
        } 
        return 0; 
    }
    

    das continue dürckt mehr sofort aus, nämlich "dieser fall ist erledigt, sofort den nächsten ausprobieren!".


  • Administrator

    Sebastian Pizer schrieb:

    Bei break und continue ist sofort klar, wo's weitergeht.

    Wo es weitergeht ist klar. Herausfinden wie der Algorithmus aussieht, kann* allerdings erschwert werden. Durch ein break kann man zum Beispiel Codeduplizierung entfernen, wie es tntnet vorgeführt hat. Dadurch hast du aber eben irgendso eine halbe Schleife. Um die Schleife zu verstehen, muss du sie in deinem Kopf wieder entpacken und hast dann im Kopf die Codeduplizierung. Daher ist auch Codeduplizierung nicht einfach nur grundlegend schlecht und wenn man das dann noch in Funktionen aufteilt oder auf Variablen verteilt (z.B. die message Variable in meinem vorherigen Code), dann sehe ich eher Vorteile in der Lesbarkeit.

    *Ich möchte, dass man dieses kann nicht vergisst: kann werden != wird 😉

    Grüssli



  • Gesprungen wird immer irgendwo irgendwie, zB auch beim Funktionsaufruf.

    Der Unterschied beim Funktionsaufruf ist, dass dieser bewusst implementierungsdetails versteckt.
    Im Gegensatz zu Sprunganweisungen wird der Lesefluss daher nicht unterbrochen.

    und break hat insofern den selben Sucheffekt wie goto, weil du nun nicht mehr beim lesen des Schleifenkopfes sagen kannst, wann die Schleife verlassen wird.


Anmelden zum Antworten