while Schleife mit Counter und character Ausgabe



  • Hallo,

    ich hoffe mal die Überschrift ist nicht irreführend ^^. Also unser prof hat folgende Anforderung an ein Programm gestellt.

    Einfaches Histogramm erstellen
    l Schreiben Sie ein Programm, das fünf Zahlen (jede zwischen 1 und 80) von der Konsole einliest. Für jede eingegebene Zahl soll
    Ihr Programm eine Zeile mit entsprechend vielen aneinander hängenden Sternchen (*) ausgeben. Zum Beispiel soll für eine
    eingegebene Zahl 7 folgende Zeile ausgegeben werden:
    *******

    Soweit so gut. Leider scheint meine Schleife ins endlose zu verlaufen, obwohl ich keinen Fehler in meiner Bedingung finden kann.

    Desweiteren weiss ich nicht, wie ich mit cin einlesen könnte (ich kenne eigentlich nur die Parameter Übergabe wenn ich bei der objekterstellung einen Fixen wert in die Klammer eingebe)

    meine Quelldateien:

    histogramm.h

    #include <string>
    using std::string;
    
    class histogramm
    {
    public:
       histogramm( int ); 
       void setZahl( int );
       int getZahl();
       void displayMessage();
    
    private:
       int zahl;
       int counter;
       int i;
    };
    

    histogramm.cpp

    #include <iostream>
    using std::cout; 
    using std::cin;
    using std::endl;
    
    #include "histogramm.h"
    
    histogramm::histogramm( int number )
    {
       setZahl( number );
    }
    
    void histogramm::setZahl( int number )
    {
    	zahl = number;
    }
    
    int histogramm::getZahl()
    {
       return zahl;
    }
    
    void histogramm::displayMessage()
    {
    	counter = i++;
    	while(counter <zahl)
    		{ cout << "*";}
    
    }
    

    main.cpp

    #include <iostream>
    using std::cout; 
    using std::endl;
    
    #include "histogramm.h"
    
    int main()
    {
       histogramm histogramm1 ( 5 );
      // histogramm histogramm2 ( 10 );
      // histogramm histogramm3 ( 15 );
      // histogramm histogramm4 ( 20 );
      // histogramm histogramm5 ( 25 );
    
       histogramm1.displayMessage();
      // histogramm2.displayMessage();
      // histogramm3.displayMessage();
      // histogramm4.displayMessage();
      // histogramm5.displayMessage();
    
       return 0;
    }
    

    Derzeit läuft die Ausgabe von * endlos, dabei sollte doch ducrh Aufruf von histogramm1 zahl auf 5 gesetzt werden und die Ausgabe stoppen, wenn counter nicht mehr kleiner gleich zahl ist.

    Ich raffs net 😞



  • Hi,

    ich verstehe nicht ganz, warum du so kompliziert an die Sache rangehst.
    Eine einfach for-Schleife tut's hier doch auch:

    ..
    
    int user_input;
    cin>>user_input;
    for(i=0;i<=user_input;i++)
    {
        cout << * << endl;
    }
    ...
    

    Irgendwie sowas in der Art eben...

    ~Edit: Aufgabenstellung nicht durchgelesen, sorry. Also vergiss den Beitrag^^~



  • Das Objektorientierung in diesem Fall mit der Kanone auf Spatzen geschossen ist sollte klar sein:

    #include <vector>
    #include <iostream>
    
    using namespace std;
    
    template< typename T >
    class set_t;
    
    template< typename T >
    istream& operator>>( istream& is, set_t< T > &set ) {
    
        T temp;
    
        for( size_t i = 1; i <= set.size; ++i ) {
    
            cout << i << ". Wert: ";
            is >> temp;
    
            if( is.fail( ) || temp < set.min || temp > set.max ) {
    
                is.clear( ios_base::failbit );
                return is;
            }
    
            set.m_data.push_back( temp );
        }
        return is;
    }
    
    template< typename T >
    class set_t {
    
        friend istream& operator>> <> ( istream& is, set_t< T > &set );
    
        private:
    
            vector< T > m_data;
            T m_min, m_max;
            size_t m_size;
    
        public:
    
            const T &min, &max;
            const size_t &size;
    
            set_t( size_t size ) :
    
                m_min( numeric_limits< T >::min( ) ),
                m_max( numeric_limits< T >::max( ) ),
                m_size( size ),
                min( m_min ),
                max( m_max ),
                size( m_size )
    
            {
    
                m_data.reserve( size );
                m_size = size;
            }
    
            ~set_t( ) { }
    
            void interval( T min, T max ) {
    
                if( min >= numeric_limits< T >::min( ) ) {
    
                    m_min = min;
                }
    
                if( max <= numeric_limits< T >::max( ) ) {
    
                    m_max = max;
                }
            }
    
            T at( size_t i ) const {
    
                return m_data[ i ];
            }
    };
    
    template< typename T >
    class histogram_t;
    
    template< typename T >
    ostream& operator<< ( ostream& os, const histogram_t< T > &histogram ) {
    
        os << "Histogramm von " << histogram.size << " Werten:" << endl << endl;
    
        for( size_t i = 0; i < histogram.size; ++i ) {
    
            for( T n = 0; n < histogram.set.at( i ); ++n ) {
    
                os << histogram.m_character;
            }
    
            os << endl;
        }
    
        return os;
    }
    
    template< typename T >
    class histogram_t {
    
        friend ostream& operator<< <> ( ostream& os, const histogram_t< T > &histogram );
    
        private:
    
            char m_character;
            set_t< T > m_set;
            const size_t m_size;
    
        public:
    
            const set_t< T > &set;
            const size_t &size;
    
            histogram_t( const set_t< T > &set, char character = '*' ) :
    
                m_character( character ),
                m_set( set ),
                m_size( set.size ),
                set( m_set ),
                size( m_size )
            {
            }
    
            ~histogram_t( ) { }
    };
    
    int main( ) {
    
        set_t< int > set( 5 ); // create a set to hold up to five values
    
        set.interval( 1, 80 ); // set the interval for which values are valid
    
        // beg the user for data
    
        cout << "Bitte geben Sie " << set.size << " ganzzahlige Werte im Interval [ ";
        cout << set.min << ", " << set.max << " ] ein:" << endl << endl;
    
        cin.clear( );
    
        do {
    
            if( cin.fail( ) ) { // error handling
    
                cout << "Eingabefehler!" << endl;
                cout << "Bitte wiederholen Sie Ihre Eingaben:" << endl << endl;
    
                cin.clear( );
                cin.ignore( numeric_limits< int >::max( ),'\n' );
            }
    
            cin >> set; // user input
    
        } while( cin.fail( ) );
    
        histogram_t< int > histogram( set ); // create a histogram based on the users set
    
        cout << endl;
        cout << histogram << endl; // print the histogram
    }
    

    Greetz, Swordfish

    PS: Du solltest Dich schleunigst mit I/O beschäftigen!



  • ThaRealMatix schrieb:

    void histogramm::displayMessage()
    {
    	counter = i++;
    	while(counter <zahl)
    		{ cout << "*";}
    

    Bei deiner while schleife wird nichts erhöht um die schleife irgendwann einmal zu beenden.

    probiers mal hiermit:

    int counter = 0;
    while(counter < zahl)
    {
    cout <<"*";
    counter++;
    }
    


  • megaweber schrieb:

    [...]

    Bei deiner while schleife wird nichts erhöht um die schleife irgendwann einmal zu beenden.

    [...]

    Stimmt natürlich, aber imvho fehlen der realen Matrix einfach ein paar grundlegendste Grundlagen... Der gesamte Ansatz ist Käse. 🙄

    Greetz, Swordfish



  • Jo 8 - 10 Zeilen Code hätten's auch getan



  • megaweber schrieb:

    Jo 8 - 10 Zeilen Code hätten's auch getan

    naja,

    #include <iostream>
    
    using namespace std;
    
    const size_t num_values = 5;
    
    int main( ) {
    
        int values[ num_values ];
    
        for( size_t i = 0; i < num_values; ++i ) {
    
            cout << i <<". Wert: ";
            cin >> values[ i ];
        }
    
        cout << endl << "Histogram:" << endl << endl;
    
        for( size_t i = 0; i < num_values; ++i ) {
    
            for( int n = 0; n < values[ i ]; ++n )
    
                cout << '*';
    
            cout << endl;
        }
    
        cout << endl;
    }
    

    ist wohl das geringste, was jemand bei dieser Aufgabenstellung durchgehen lässt.

    Greetz, Swordfish



  • megaweber schrieb:

    Jo 8 - 10 Zeilen Code hätten's auch getan

    Jo, stimmt! Ist aber dann kein Anfänger-Variante mehr.

    #include <iostream>
    #include <algorithm>    // std::fill_n
    #include <iterator>     // std::ostream_iterator
    #include <iomanip>      // std::setw
    
    std::ostream& bar( std::ostream& out )
    {
        std::fill_n( std::ostream_iterator< char >( out ), out.width( 0 ), '*' );
        return out;
    }
    
    int main()
    {
        using namespace std;
        for( int i=0; i<5; ++i )
        {
            int zahl;
            if( cin >> zahl && zahl < 80 )
                cout << setw( zahl ) << bar << endl;
        }
        return 0;
    }
    

    Gruß
    Werner



  • Warum denn so umständlich?

    #include <iostream>
    #include <iomanip>      // std::setw
    
    int main()
    {
        using namespace std;
        for( int i=0; i<5; ++i )
        {
            int zahl;
            if( cin >> zahl && zahl < 80 )
                cout << setw( zahl ) << setfill('*')<< '*' << endl;
        }
        return 0;
    }
    

    PS: Wird das jetzt ein Wettbewerb ala "wer schafft die Aufgabe mit dem schönsten STL-Trick?"



  • CStoll schrieb:

    Warum denn so umständlich?

    ... weil das auch mein erster Gedanke war und ich diese Lösung wieder verworfen habe, da das setfill('*') wieder zurückgesetzt werden sollte. Und mit boost.io_state_saver wollte ich nicht auch noch anrücken.

    CStoll schrieb:

    PS: Wird das jetzt ein Wettbewerb ala "wer schafft die Aufgabe mit dem schönsten STL-Trick?"

    Ja, macht doch Spaß - oder? :xmas1:

    Gruß
    Werner



  • Werner Salomon schrieb:

    CStoll schrieb:

    Warum denn so umständlich?

    ... weil das auch mein erster Gedanke war und ich diese Lösung wieder verworfen habe, da das setfill('*') wieder zurückgesetzt werden sollte. Und mit boost.io_state_saver wollte ich nicht auch noch anrücken.

    Ja, aber das dürfte auch nicht das Problem darstellen? Ein char f=cout.fill(); am Anfang und cout<<setfill(f); am Ende ist wesentlich einfacher zu verstehen als dein Manipulator-Gebastel (sorry, wenn "Gebastel" abwertend klingt).



  • CStoll schrieb:

    (sorry, wenn "Gebastel" abwertend klingt).

    Ja, es klingt abwertend. Du hättest das 'Gebastel' ja auch ohne Informationsverlust weglassen können.

    Aber ich wünsche Dir trotzdem eine schöne Vor-Weihnachtszeit, auf das sich Deine Laune bessern möge. :xmas1:

    Gruß
    Werner



  • Hm...

    also um mal hier zu erklären, wieso ich auf diese Weise heran gegangen bin.
    Wir hatten in der Vorlesung davor nun einmal Klassen durchgenommen. Ich habe hier wieder OOP genommen, weil ich einfach annehme, das unser Prof sich erhofft, das wir

    a) damit arbeiten um darin sicherer zu werden
    b) das Programm recht schnell erweitern können sollen um x Eingegebene Zahlen

    Er erwartet ja in der Aufgabenstellung 5 Zahlen und ich hab halt gedacht, das es einfacher wäre eine Funktion zu definieren und diese dann per Objekt immer wieder neu aufzurufen.

    Es ist ja schön, hier zu erfahren, dass ich anscheinend der letzte Idiot bin, eigentlich hatte ich gedacht, dass meine Lösung, so man Sie mit OOP realisieren muss zumindest nicht sau mieß wäre, zumal ich mir wirklich Mühe gegeben hatte mit dem in der Vorlesung gelernten zu arbeiten - und ich dachte ich übersehe nur etwas.

    Ich hatte mir mit diesem Posting erhofft zu hören, wo mein Denkfehler oder Ansatzfehler ist. Ich hätte mich sicherlich darüber gefreut, wenn man mir anhand meines Codes aufzeigt wo ich Fehler vermeiden könnte oder ich schlechte Ansätze habe, Komplett Programme von Leuten die viel mehr drauf haben als ich helfen mir da absolut nicht weiter. Das einzige was mir hier wohl weiterhilft, ist die Aussage das ich mich nochmal mit Input Output beschäftigen sollte, den ich dankend (ja wirklich und ohen Ironie) annehme.

    Es tut mir leid, dass ich Euren hohen Coding Ansprüchen nicht genüge. Es tut mir weiterhin leid das ich nach 7 Wochen nicht so weit bin wie ich vielleicht sollte , ich werde mich bemühen meine Anstrengungen in anderen Vorlesungen und meinem Nebenjob zu reduzieren um mich völligst aufs Programmieren konzentrieren zu können. Bis dahin werde ich Euch den gefallen tun, und Euer Forum nicht weiter mit meiner Unwissenheit und Müllcode zu behelligen

    Herzlichst

    Gunnar



  • Nur weil man in C++ objektorientiert programmieren kann, muß man noch lange nicht alles als Objekt darstellen. (und meistens führt es nur zu unnötigen Verwirrungen, wenn man es versucht)

    Wenn du der Meinung bist, das ursprüngliche Programm erweitern zu wollen, könntest du als erstes das Array durch einen vector<> ersetzen (schon bist du in der Lage, beliebig viele Werte einzulesen und darzustellen).

    @Werner: Ich hab' mich ja entschuldigt :xmas2:



  • Hm...

    ich habe die Aufgabenstellung ja am Anfang erwähnt. Ich weiss nicht ob ich objektorientiert arbeiten muss ich habs einfach probiert, weil ich selbst eben kein Crack bin und dachte das ich damit nun sicherer darin werde.

    Mir ist völlig klar, dass dass Programm für diese Aufgabe übertrieben ist.

    Ich kann mich jetzt spontan auch nicht erinnern, schon Vektoren durch genommen zu haben.

    Mir würde es für's erste wirklich genügen, wenn man mir sagt wo ich in meinem Code Dinge - mit den Mitteln die ich eingesetzt habe bzw einsetzen muss - Patzer habe.

    Desweiteren ist mir halt absolut nicht klar wie ich eine cin Eingabe in die "Objekterstellung" ( histogramm histogramm1 ( 5 );) bekommen könnte.

    Ich bin ja bemüht mir c++ anzueignen und lese das Script sowie ein Buch - leider muss ich auf das Buc h das der Prof verwendet aber noch warten - und bin bemüht nicht bei jeder Kleinigkeit hier sofort anzufragen, aber irgendwann brauch ich halt Hilfe. Wenn dieses Forum nicht dazu gedacht ist Anfängern auch bei belanglosem vielleicht erklärend zur Seite zu stehen, ist das für mich in Ordnung, es wäre nur nett wenn man mir den Anspruch dieses Forums dann mitteilt - ich suche mir dann gerne eines für Anfänger, kein Thema.

    Grüße

    Gunnar

    P.S ich werde natürlich gerne auch nocheinmal probieren, eine einfachere Lösung zu Programmieren



  • ThaRealMatix schrieb:

    Mir würde es für's erste wirklich genügen, wenn man mir sagt wo ich in meinem Code Dinge - mit den Mitteln die ich eingesetzt habe bzw einsetzen muss - Patzer habe.

    Der auffälligste Patzer wurde ja schon erwähnt - du hast die displayMessage() falsch aufgebaut und erzeugst dadurch eine Endlosschleife.

    (außerdem ist es unnötig, die Variablen counter und i in der Klasse anzulegen - die werden nur von displayMessage() benötigt, können also auch lokal in der Methode angelegt werden)

    Desweiteren ist mir halt absolut nicht klar wie ich eine cin Eingabe in die "Objekterstellung" ( histogramm histogramm1 ( 5 );) bekommen könnte.

    Im einfachsten Fall so:

    int val;
    histogramm data[5];//Array für fünf histogramm'e
    for(int i=0;i<5;++i)
    {
      cin>>val;
      data[i].setZahl(val);
    }
    

    (als Erweiterung würde ich der Klasse noch einen op>> übergeben)



  • Hm... sehe ich das richtig das das nun in die setZahl käme? Wenn ich aber das nun so wie du mache, muss ich dann noch 5 objekte erzeugen? Mich verwirrt dieses

    histogramm data[5];
    

    heisst das das er ohnehin fünf Werte nacheinander eingegeben haben will?

    Tut mir leid für die doofe Frage aber ich hab bis jetzt erst einmal nen Array benutzt und das war als ich Sekunden als Worte ausgeben lassen wollte - und das ist lange her.



  • ThaRealMatix schrieb:

    Hm... sehe ich das richtig das das nun in die setZahl käme? Wenn ich aber das nun so wie du mache, muss ich dann noch 5 objekte erzeugen? Mich verwirrt dieses

    histogramm data[5];
    

    Das erzeugt ganz einfach ein Array mit fünf Elementen (die du über ihren Index als 'data[0]' bis 'data[4]' ansprechen kannst. (alternativ müsstest du fünf einzelne Objekte anlegen und dann alle Befehle fünfmal hintereinander schreiben - das sieht scheußlich aus, ist nicht erweiterbar und saumäßig zu warten)



  • ThaRealMatix schrieb:

    ... sehe ich das richtig das das nun in die setZahl käme?

    Ja. Dazu hast Du ja die (selbst geschriebene) Methode setZahl.

    ThaRealMatix schrieb:

    ... heisst das das er ohnehin fünf Werte nacheinander eingegeben haben will?

    Nein er erstellt nur fünf Objekte. Befüllt werden sie über die for-Schleife

    for (int i = 0; i < 5; ++i)
    {
        // ...
    

    Hoffe das hilft Dir weiter...

    Viele Grüße und viel Erfolg
    Knecht



  • ThaRealMatix schrieb:

    ...Wenn dieses Forum nicht dazu gedacht ist Anfängern auch bei belanglosem vielleicht erklärend zur Seite zu stehen, ist das für mich in Ordnung, es wäre nur nett wenn man mir den Anspruch dieses Forums dann mitteilt - ich suche mir dann gerne eines für Anfänger, kein Thema....

    Ho, Ruhig, Brauner - niemand hat Dich hier "angemacht". 😉 :xmas1:

    Aber als Einsteiger wirst Du evtl. auch zu schätzen wissen, wenn Dir hier nicht nur auf dem Weg zu einer korrekten Programmsyntax, sondern auch zu einer angemessenen Programmiertechnik geholfen wird.

    ... und dazu zählt der Hinweis, dass es eine Frage der Problemstellung ist, ob OOP geeigneter ist oder evtl. prozedural. Das bedeutet nicht, dass es in OOP nicht ginge, aber zu demonstrieren, wie und warum prozedural einfacher ist, ist nicht als Vorwurf gemeint.

    Gruß,

    Simon2.



  • .. hoppla, der Server war weg. Daher kommt die Antwort etwas später:

    ThaRealMatix schrieb:

    ich habe die Aufgabenstellung ja am Anfang erwähnt. Ich weiss nicht ob ich objektorientiert arbeiten muss ich habs einfach probiert, weil ich selbst eben kein Crack bin und dachte das ich damit nun sicherer darin werde.

    Mir ist völlig klar, dass dass Programm für diese Aufgabe übertrieben ist.

    Ich kann mich jetzt spontan auch nicht erinnern, schon Vektoren durch genommen zu haben.

    Mir würde es für's erste wirklich genügen, wenn man mir sagt wo ich in meinem Code Dinge - mit den Mitteln die ich eingesetzt habe bzw einsetzen muss - Patzer habe.

    Desweiteren ist mir halt absolut nicht klar wie ich eine cin Eingabe in die "Objekterstellung" ( histogramm histogramm1 ( 5 );) bekommen könnte.

    Hallo Gunnar,

    um noch mal eine Lanze für die Objektorientierung zu brechen ... Es ist auch in diesem Fall völlig ok, es mal zu probieren. An solch' kleinen Beispielen kann man schön üben.
    Wenn man eine Klasse entwirft, sollte man sich (vorher!) darüber klar werden, welchen Zweck so eine Klasse hat. Damit man nicht in die Falle rennt, die CStoll schon erwähnt hat. In diesem Fall wäre der Zweck einfach die Ausgabe des *-Balkens.
    Jetzt muss man noch wissen, wie man das in C++ realisiert. Hier wäre eine friend-Funktion mit überladenem operator<< die übliche Lösung. Also

    class histogramm {
    public:
        friend std::ostream& operator<<( std::ostream& out, const Histogramm& h )
        {
            // und hier jetzt h.zahl mal '*' ausgeben
            return out;
        }
    };
    

    Damit kann man ein Histogramm genauso ausgeben, wie etwa ein int:

    histogramm h ...;
    cout << h << endl;
    

    Die Methode displayMessage ist dann hinfällig. Ein Patzer ist es auch, wenn Du innerhalb von displayMessage die Ausgabe auf cout lenkst. Damit schränkst Du den Anwender in der Wahl des Ausgabe-Mediums ein; in solchen Fällen immer den std::ostream mitgeben - genau wie bei der friend-Funktion oben - damit der Anwender sich das Ziel der Ausgabe aussuchen kann (cout, ofstream oder was anderes).
    Weiter sollte man im Konstruktor immer die Initialisierung-Listen verwenden und mit get- und set-Methoden sparsam umgeben. Ich meine, in diesem Fall brauchst Du keine Get/Set-Methode. Auch beim Einlesen könntest Du notfalls ein Histogramm einfach zuweisen (genau wie ein int), statt ein setZahl zu benutzen.

    histogramm h[5];
    for( int i=0; i<5; ++i )
    {
        int val;
        cin >> val;
        h[i] = Histogramm( val );
    }
    

    Der Unterschied zwischen einem setZahl und einer Zuweisung ist der, dass Du keinerlei interne Strukturen der Klasse Histogramm für den Anwender offen legen musst. Das nennt man Kapselung der Daten. In diesem Fall mag der Vorteil nicht so offensichtlich sein, dazu ist die Anwendung zu klein, aber grundsätzlich hat dies große Vorteile.

    Ja man könnte auch noch einen Schritt weitergehen (nur so als Übung - ich weiß schon: Kanonen & Spatzen ..), indem man Histogramm 'eingebbar' macht. Also das Ziel kann es sein, folgendes zu schreiben:

    Histogramm h[5];
    for( int i=0; i<5; ++i )
    {
        cin >> h[i];
    }
    

    Das geht symmetrisch zur Ausgabe mit einem zusätzlichen operator>>.

    class Histogramm {
    public:
        friend std::istream& operator>>( std::istream& in, Histogramm& h )
        {
            int val;
            if( in >> val )
                h.zahl = val;
            return in;
        }
    };
    

    auch hier wieder nicht von std::cin einlesen - etwa mit einer Methode Histogramm::eingabe() - sondern allgemein von einem std::istream lesen lassen und erst bei der Anwendung (s.o.) wird das Medium (cin) festgelegt.

    Gruß
    Werner


Anmelden zum Antworten