Aufgabe gut gelöst?
-
Hallo,
ich habe die Übung 4 aus dem Kapitel 4 des Buches "Einführung in die Programmierung mit C++" von Stroustrup umgesetzt, leider scheint es zu der Aufgabe keine Musterlösung zu geben.
Die Aufgabe war ein Programm zu schreiben, das in max. 7 Versuchen eine Zahl von 1 bis 100 errät.
Mein Programm scheint zu tun was es soll, ich bin mir aber nicht sicher ob mein Versuch eine effiziente / gute Lösung für das Problem ist und bitte um entsprechende Kommentare / Kritik.
#include <iostream> #include <string> using namespace std; string ask(int tipp) { string answer = " "; cout << "Ist die gesuchte Zahl größer, kleiner oder gleich " << tipp << "?: "; cin >> answer; while(answer != "größer" && answer != "kleiner" && answer != "gleich") { cout << "\nEingabe nicht erkannt!\n"; cout << "Ist die gesuchte Zahl größer, kleiner oder gleich " << tipp << "?: "; cin >> answer; } return answer; } int main() { cout << "Zahlen Raten\n\n"; cout << "Denken Sie sich eine Zahl von 1 bis 100 aus\n\n"; int is_bigger = 0; int is_smaler_or = 101; int tipp = 0; string answer = " "; // "größer", "kleiner" oder "gleich" while(answer != "gleich") { if(is_smaler_or - is_bigger > 2) { tipp = ((is_smaler_or - is_bigger) / 2) + is_bigger; answer = ask(tipp); if(answer == "größer") { is_bigger = tipp; } else if(answer == "kleiner") { is_smaler_or = tipp; } else { cout << "Die gesuchte Zahl ist " << tipp << '\n'; } } else { cout << "Die gesuchte Zahl ist " << is_smaler_or - 1 << '\n'; answer = "gleich"; } } }
-
Du bist auf die binäre Suche gekommen, sieht doch gut aus.
Nur sowas ist ungewöhnlich: while(answer != "gleich")
Bei Stringvergleichen zerbricht man sich leicht die Logik, wenn man etwas an der Ausgabe ändert.
-
Sieht recht gut aus. Folgendes sind ist kein Mecker, sondern nur unverbindliche Vorschläge oder Haarspalterei.
#include <iostream> #include <string> using namespace std; string ask(int tipp)//sollte int zurückgeben wie strcmp { string answer = " ";//Nutzlose Initialisierung, laß leer! cout << "Ist die gesuchte Zahl größer, kleiner oder gleich " << tipp << "?: "; cin >> answer; while(answer != "größer" && answer != "kleiner" && answer != "gleich") {//Bedingung ist recht cout << "\nEingabe nicht erkannt!\n"; cout << "Ist die gesuchte Zahl größer, kleiner oder gleich " << tipp << "?: ";//Viel Code dupliziert cin >> answer;//dito //Habe ein Pascal-Dejavu } return answer; } int main() {//Uneinheitlicher Klammerstil. Warum nicht oben wie bei Kontrollstrukturen? cout << "Zahlen Raten\n\n"; cout << "Denken Sie sich eine Zahl von 1 bis 100 aus\n\n"; int is_bigger = 0;//Ganz schlechter Name. Ein is_* ist ein bool. int is_smaler_or = 101; int tipp = 0;//HALT! Nutztlose Initialisierung mit 0. Ein Zeichen dafür, daß die //Variable einfach zu unlokal ist. string answer = " "; // "größer", "kleiner" oder "gleich"//HALT! Kommentar nötig? Falls ja, ist der Code selber nicht selbsterklärend. //Erstmal versuchen, den Code zu reparieren. while(answer != "gleich") {//Uneinheitlicher Klammerstil. Warum nicht unten wie bei Funktionen? //Evtl for(;;) und die Introspektion komplett innen machen. if(is_smaler_or - is_bigger > 2) {//verstehe ich nicht tipp = ((is_smaler_or - is_bigger) / 2) + is_bigger;//Die Klammern verwirren mich answer = ask(tipp);//Och nöö. Soll auch lokaler sein. Daher der Schmerz bei =" ". if(answer == "größer") { is_bigger = tipp; } else if(answer == "kleiner") { is_smaler_or = tipp; } else { cout << "Die gesuchte Zahl ist " << tipp << '\n'; //return, siehe unten } } else { cout << "Die gesuchte Zahl ist " << is_smaler_or - 1 << '\n'; answer = "gleich";//Hä? Och nöö. break wäre erträglich, return wäre viel besser, aber dazu müßte die //Raterei aus der main() raus. } } }edit: Ich bezweifle, daß hier ein halboffenes Intervall angebracht ist.
-
Vielen Dank für eure Antworten.
Den Hinweis in Zeile 35 verstehe ich leider nicht, was meinst du mit "soll lokaler sein"?
In dem Buch werden Variablen (bisher) in den Code-Schnipseln eigentlich immer Initialisiert, selbst wenn sie z.B. 2 Zeilen später durch eine Eingabe einen neuen Wert zugewiesen bekommen. Ich denke solange ich das Buch durcharbeite halte ich mich besser an das was dort gezeigt wird.
Was die geschweiften Klammern angeht, auch hier zieht sich durch das gesamte Buch das bei Funktionen und Klassen die öffnende Klammer in der nächsten Zeile steht und bei Kontrollstrukturen in der selben Zeile. Ich weiß das dass eine Stil-Frage ist, so wie ich es schreibe ist es z.B. auch in Stroustrup's PPP Style Guide vorzufinden.
Die übrigen Hinweise finde ich allesamt sehr Hilfreich, vielen Dank.
In meiner ask Funktion eine do-while Schleife zu verwenden anstatt Code zu duplizieren, daran hätte ich auch selbst denken können.

-
NilsV schrieb:
Den Hinweis in Zeile 35 verstehe ich leider nicht, was meinst du mit "soll lokaler sein"?
Tu normalerweise Variablen so spät definieren, wie irgend möglich.
-
So wie ich das geschrieben habe, benötigte ich die Variable ja schon in der nächsten Zeile als Bedingung für die While-Schleife.
Ich würde da auch lieber Prüfen ob tipp richtig ist, dafür müsste ich meine ausgedachte Zahl aber am Anfang eingeben (Würde das Raten irgendwie witzlos machen).
[EDIT]:
Sorry, hab mich voll vertan. Du meintest tipp, ich war irgendwie bei answer. Hab mich da vertan, nun ist alles klar.
-
NilsV schrieb:
In dem Buch werden Variablen (bisher) in den Code-Schnipseln eigentlich immer Initialisiert, selbst wenn sie z.B. 2 Zeilen später durch eine Eingabe einen neuen Wert zugewiesen bekommen.
Die Eingabe in C++ ist leider sehr uncool. Bei der Eingabe gibt es keinen schönen Weg.
NilsV schrieb:
Ich denke solange ich das Buch durcharbeite halte ich mich besser an das was dort gezeigt wird.
Das denke ich nicht. Bei Stilfragen wie den Klammern klar. Aber Variablen lokaler machen ist echt klasse und answer=" " ist echt unklasse. Warum nicht answer="sigma phi gamma"?
-
Für den Vergleich von Strings gibt es dazu bestimmte Memberfunktionen (compare()).
Und wo wird die Anzahl von Versuchen gezählt? Für die Aufgabe ist diese Größe wichtig, also solltest du sie auch überprüfen.Meine persönliche Meinung: die letzte Überprüfung in der while-Schleife ist überflüssig, da dies durch die Schleifenbedingung gewährleistet wird.
Ich würde einfach am Ende die Zahl ausgeben:[...] while(answer != "gleich") { if(is_smaler_or - is_bigger > 2) { tipp = ((is_smaler_or - is_bigger) / 2) + is_bigger; answer = ask(tipp); if(answer == "größer") { is_bigger = tipp; } else if(answer == "kleiner") { is_smaler_or = tipp; } } else { cout << "Die gesuchte Zahl ist " << is_smaler_or - 1 << '\n'; answer = "gleich"; } } cout << "Die gesuchte Zahl ist " << tipp << '\n'; [...]Noch ein kleiner Tipp: ich weiß nicht, ob du das schon hattest, aber mit dem arithmetischen IF (also ? : ) man kann einfache Überprüfungen und darauf folgende einzeilige Prozeduren sehr schön miteinander "verschmelzen". Hier ist es aber von geringer Bedeutung.
-
NilsV schrieb:
So wie ich das geschrieben habe, ...
Tja. Probier's mal mit dem häßlichen for(;;). Ob das damit echt hübscher wird, muß man mal schauen. Kann sein, daß die unlokale Variable und while das kleinere Übel ist. Aber man sieht es erst genau, wenn man es ausprobiert und Folgeoptimierungen (falls möglich) ausführt.
-
[Rewind] schrieb:
Für den Vergleich von Strings gibt es dazu bestimmte Memberfunktionen (compare()).
Aber == ist angemessener.
[Rewind] schrieb:
Und wo wird die Anzahl von Versuchen gezählt? Für die Aufgabe ist diese Größe wichtig, also solltest du sie auch überprüfen.
Nö. Zählen ist nicht nötig.
[Rewind] schrieb:
Noch ein kleiner Tipp: ich weiß nicht, ob du das schon hattest, aber mit dem arithmetischen IF (also ? : ) man kann einfache Überprüfungen und darauf folgende einzeilige Prozeduren sehr schön miteinander "verschmelzen".
Du meinst "sehr häßlich". Meiner Meinung nach könnhte man den ?: auch abschaffen. Das würde viel Unfug verhindern. Ich verwende ?: seltener als goto oder mehrfache Erblichkeit. So schlimm ist das.
-
Außerdem schreibt man tip mit nur einem p und smaller mit zwei l.
Vielleicht ist der der unglückliche Name is_smaler_or zurückzuführen darauf, daß Dein Englisch-Wortschatz nicht so mächtig fett ist, wie dein deutscher? Dann wäre es doch schlau, die Bezeichner auf deutsch zu schreiben, um treffendere Namen zu finden und das Augenmerk ohne externe Behinderung auf extrem hübsches Programmieren legen zu können.
-
answer = "gleich";//Hä? Och nöö. break wäre erträglich, return wäre viel besserWeder continue / break noch das man mehrere return Anweisungen haben kann, kam im Buch bisher dran. Ich kenne die Sachen zwar (Hab mir vorher schon ein paar Grundlagen angeeignet), denke aber das es im Sinne der Aufgaben ist, mit dem zu arbeiten was das Buch einem bisher erklärt hat.
-
NilsV schrieb:
Sorry, hab mich voll vertan. Du meintest tipp, ich war irgendwie bei answer. Hab mich da vertan, nun ist alles klar.
Nein.
tipp meinte ich natürlich. Die schreit ja danach, lokaler zu werden.Aber answer meinte ich auch!

Aber auf Kosten der Schleifenbedingung und mit ersatzuweisem if() break; vielleicht.
Es würde die Variable lokaler machen (nicht so wichtig) und die Untersuchung stärker zusammenhalten (schon wichtiger), statt sie so ausgefranst zu haben, ein teil im while, ein Teil im, Schleifenkörper und dort sogar mit verschachteltem if.
Gehts mit geeignetem break bzw continue nicht irgendwie, das verschachtelte if loszuwerden?
-
NilsV schrieb:
Weder continue / break noch das man mehrere return Anweisungen haben kann, kam im Buch bisher dran. Ich kenne die Sachen zwar (Hab mir vorher schon ein paar Grundlagen angeeignet), denke aber das es im Sinne der Aufgaben ist, mit dem zu arbeiten was das Buch einem bisher erklärt hat.
Dann lies das Buch im Sondereiltempo jetzt durch bis hinten hin. Und dann lies es zum zweiten mal normal und bearbeite die Aufgaben, diemal aber mit Überblick.
Es kann nicht Sinn der Sache sein, daß Du Dir jetzt einen Kindergartenstil angewöhnst.break und continue gehören untrennbar zu Schleifen. mehrere returns gehören untrennbar zu Funktionen. Das halte ich nicht für was austauschbares, wie ob man std::find_if benutzt oder selber eine Suchschleife nimmt, weil man std::find_if nocht kennt oder nicht mag.
-
NilsV schrieb:
answer = "gleich";//Hä? Och nöö. break wäre erträglich, return wäre viel besserWeder continue / break noch das man mehrere return Anweisungen haben kann, kam im Buch bisher dran. Ich kenne die Sachen zwar (Hab mir vorher schon ein paar Grundlagen angeeignet), denke aber das es im Sinne der Aufgaben ist, mit dem zu arbeiten was das Buch einem bisher erklärt hat.
Der Sinn solcher Aufgaben ist es, dass man sie immer versucht so gut wie irgendmöglich zu lösen. Es steht zwar nie in den Büchern drin, aber die Autorne wollen auch zum einen, dass man auch vor-ließt, zum anderen, dass man die Aufgaben wiederholt mit dem neuen Wissen löst.
Das Problem an diesen Aufgaben ist, dass sie Plakativ irgendwelche Sprachfeatures zeigen sollen. Wenn im Buch switch dran kommt, dann wird irgendeine Aufgabe genommen in der sich Switch anbietet. In der Praxis ersetzt du aber Switch eigentlich immer durch andere, bessere Konstruktionen. Die Aufgabenlösungen mit den bekannten Features stellen also kein Optimum da.
-
@volkard: Was hast du denn gegen ?: ?
Ich stimme dir zu, dass er zu oft verwendet wird, aber gelegentlich ist er doch ganz praktisch.