C++ - Aufgabe: Programmierer gesucht. Gegen Geld!



  • Hallo in die Runde,
    ich bin auf der Suche nach Jemanden, der kurzfristig (ich sage mal so in den nächsten 3 oder 4 Tagen), was kleines machen kann. Es handelt sich um eine kleine "Problemstellung", die nun schon mit einer anderen Sprache umgesetzt wurde, nun aber in C++ noch geschrieben werden muss. Leider fehlt mir die Zeit eben, meine wirklich verrosteten C++-Kenntnisse aufzufrischen, da das Ding quasi "gestern schon" fertig sein sollte.
    Ich würde gerne die Problemstellung hier mal reinstellen und zahle natürlich auch die Unkosten, die anfallen. Es handelt sich hierbei um eine Übungsaufgabe für Studenten, die das nun mit C++ machen sollen. Leider musste ich kurzfristig einspringen und wie schon erwähnt, bin ich für meinen Teil nicht so fit in C++ und müsste mir das nun mühsam aneignen.

    Die Problemstellung lautet:

    Es soll eine C++ Klasse implementiert werden, die den arithmetischen Mittelwert einer Menge von Zahlen ermittelt und entscheidet

    Die Klasse soll folgende Anforderungen erfüllen:

    - Klasse sollte default- konstruierbar sein.
    - Aktueller Mittelwert soll jederzeit abzufragen sein
    - Einzelne Werte von Zahlen sollen zur Menge hinzuzufügen sein.
    - Es soll festzustellen sein, wie viele Werte bereits zur Zahlenmenge hinzugefügt wurden.
    - Klasse soll zurückzusetzen sein, damit alle vorher eingegebenen Daten vergessen werden.

    Implementierung der C++ Klasse, die diese Anforderungen erfüllt und Instanziierung in einem Programm.

    Wer kann da kurz helfen? Bitte melden. Ich würde - nach Wunsch und Absprache - den Lohn per Paypal oder Banküberweisung direkt überweisen und hoffe natürlich dann auf ehrliche Menschen hier.
    Bestes
    Zoli


  • Mod

    Das bekommst du gratis, wenn du dich beschämt in die Ecke stellst, dass du diese 11 Zeilen nicht selber programmieren konntest.

    Ungetestet:

    class Mean
    {
      unsigned long N;
      double sum;
    public:
      Mean() : N(0), sum(0) {}
      void add(double value) { sum += value; ++N; }
      unsigned long get_count() const { return N; }
      double get_mean() const { return sum / N; }
      void clear() { sum = 0; N = 0; }
    };
    
    // Demo:
    #include <iostream>
    int main()
    {
      Mean mean;
      for (double value; std::cin >> value; mean.add(value));
      std::cout << "Mean: " << mean.get_mean() << '\n';
    }
    

    Da muss man sich ja schon zurück halten, damit man es nicht unabsichtlich besser macht, als die extrem einfache Aufgabenstellung verlangt.



  • Hallo SeppJ,
    danke für deine Hilfe und deine offenen Worte. So einfach, wie Du es aber eben geschrieben hast, ist es meiner Meinung nach nicht. Wenn ich die Aufgabenstellung richtig verstehe, sollte das Programm schon mehr können.

    Zum einen soll doch der Mittelwert jederzeit abrufbar sein. Macht man dies nicht über eine Aussage im Terminal, die dem User anzeigt, dass er bspw. durch die Eingabe eine gewissen Taste diesen Wert erhält. Genauso hinsichtlich des Löschens. Und zu guter letzt noch das Spiel für die Anzahl der hinzugefügten Werte und die Möglichkeit der Eingabe von diesen.

    Oder habe ich nun einen elementaren Denkfehler? Bin für jede Hilfe dankbar!
    Bestes
    Zoli



  • ZoliTeglas schrieb:

    Zum einen soll doch der Mittelwert jederzeit abrufbar sein.

    Dazu hat SeppJ die Funktion get_mean geschrieben.

    Oder habe ich nun einen elementaren Denkfehler?

    Ja, in der Tat. Gefordert war eine Klasse, nicht ein Programm, mit bestimmten Anforderungen. Wenn da z.B. steht "Der Mittelwert soll abrufbar sein" dann ist damit nicht gemeint, dass der Benutzer das tun kann, denn das wäre eine Aufgabe der Benutzeroberfläche des Programms, und nicht der Klasse. Sondern damit ist gemeint, dass ein anderer Programmteil, sofern er Zugriff auf das entsprechende Objekt vom Typ Mean hat, den gerade gültigen Mittelwert abfragen kann -- typischerweise über einen Methodenaufruf. Es wäre sogar sehr schlechtes Design, wenn man die Benutzerinteraktion mit in die Klasse integrieren würde.



  • 👍

    Die Demo mit main war tatsächlich auch nicht gefordert. (Und wenn schon sollte am Ende natürlich ein return stehen.)

    Von einer Benutzerschittstelle und der zugehörigen Beschreibung kann ich auch nichts in der Aufgabenstellung sehen.

    Was der unvollständige Satz

    arithmetischen Mittelwert einer Menge von Zahlen ermittelt und entscheidet

    bedeutet wäre noch zu klären.

    Über die mögliche Division durch Null sollte man auch noch mal nachdenken.

    Guter Stil wäre es die Klasse in Header und Implementation zu trennen.


  • Mod

    merano schrieb:

    (Und wenn schon sollte am Ende natürlich ein return stehen.)

    Wieso? Ist doch C++.

    Über die mögliche Division durch Null sollte man auch noch mal nachdenken.

    Ok, fertig mit Nachdenken. Es kommt NaN raus. Wie im Beispiel.

    Guter Stil wäre es die Klasse in Header und Implementation zu trennen.

    Wieso? Für die 30 Zeichen Code? Keiner davon mehr als 2-4 Assembleranweisungen? Das ist ein Vorzeigefall für sowohl Inline als auch Header only, wenn ich je einen gesehen habe.



  • Bashar schrieb:

    ZoliTeglas schrieb:

    Zum einen soll doch der Mittelwert jederzeit abrufbar sein.

    Dazu hat SeppJ die Funktion get_mean geschrieben.

    Oder habe ich nun einen elementaren Denkfehler?

    Ja, in der Tat. Gefordert war eine Klasse, nicht ein Programm, mit bestimmten Anforderungen. Wenn da z.B. steht "Der Mittelwert soll abrufbar sein" dann ist damit nicht gemeint, dass der Benutzer das tun kann, denn das wäre eine Aufgabe der Benutzeroberfläche des Programms, und nicht der Klasse. Sondern damit ist gemeint, dass ein anderer Programmteil, sofern er Zugriff auf das entsprechende Objekt vom Typ Mean hat, den gerade gültigen Mittelwert abfragen kann -- typischerweise über einen Methodenaufruf. Es wäre sogar sehr schlechtes Design, wenn man die Benutzerinteraktion mit in die Klasse integrieren würde.

    Ich dachte, dass durch den Satz

    Instanziierung in einem Programm

    gefordert wurde, dass es eben ein Programm ist, welches umgesetzt werden sollte!



  • Das hat SeppJ doch gemacht. Aber die genannten Anforderungen gelten trotzdem für die Klasse und nicht für das Programm. Wenn du unbedingt willst, kannst du ja ein Programm drumherumstricken, das es dir ermöglicht, jederzeit den aktuellen Mittelwert abzufragen -- nur war das halt nicht gefordert.



  • Das Problem ist dass hier, wie so oft, die Angabe - vermutlich - schlecht ist.
    In der Angabe steht "Implementierung der C++ Klasse, die diese Anforderungen erfüllt und Instanziierung in einem Programm".

    Dafür wäre sogar das bereits ausreichend:

    // ...
    // Implementerung von Mean
    // ...
    int main()
    {
      Mean mean; // Wir erzeugen eine Instanz. Mehr war nicht verlangt.
    }
    

    Wenn es so nicht gemeint war... tjoah, dumm, aus der Angabe kann man das aber nicht entnehmen.



  • Man sollte in get_mean prüfen, ob N ungleich 0 ist.


  • Mod

    DocShoe schrieb:

    Man sollte in get_mean prüfen, ob N ungleich 0 ist.

    Und wenn es das ist? Mein Programm macht dann schon alles richtig, ganz ohne Prüfung.



  • Ja, aber deine mean Klasse nicht. Sie lässt Fehlbenutzung ohne Fehlerbehandlung zu, das könnte man noch verbessern.


  • Mod

    DocShoe schrieb:

    Ja, aber deine mean Klasse nicht. Sie lässt Fehlbenutzung ohne Fehlerbehandlung zu, das könnte man noch verbessern.

    😕 Mein Programm lässt alles die Klasse machen. Hast du es mal ausprobiert? Welche Art von Fehler wäre N=0 und wie würdest du das behandeln, wenn nicht so, wie ich?



  • SeppJ schrieb:

    DocShoe schrieb:

    Ja, aber deine mean Klasse nicht. Sie lässt Fehlbenutzung ohne Fehlerbehandlung zu, das könnte man noch verbessern.

    😕 Mein Programm lässt alles die Klasse machen. Hast du es mal ausprobiert? Welche Art von Fehler wäre N=0 und wie würdest du das behandeln, wenn nicht so, wie ich?

    Ja, dein Programm macht alles richtig, ich habe nie etwas anderes behauptet. Das Einzige, das ich auszusetzen habe, ist, dass die Methode get_mean ohne mit der Wimper zu zucken durch 0 teilt. Das Doofe an der Division durch 0 ist, dass man die nur durch einen globalen Error Handler behandeln kann, was ziemlicher Mist ist.
    Ich würde in dem Fall entweder NaN zurückgeben oder eine exception werfen. Du kannst jetzt natürlich argumentieren, dass man vorher mit get_count() prüfen soll, ob mindestens ein Eingangswert vorliegt, aber da scheiden sich die Geister. Auf der einen Seite hat man das C++ Prinzip you only pay for what you use, auf der anderen Seite sind undefinierte mathematische Operationen blöd zu debuggen, dass ich mir da oft eine komfortablere Lösung als _matherr wünsche. Daher mein Einwand.

    Nachtrag:
    Programm starten, Eingabe "abc" => Bumm.


  • Mod

    ??? Aber es wird doch NaN zurück gegeben!



  • Jetzt haste mich soweit, ich hab´s mal übersetzt und laufen lassen. Wenn ich keine gültige Eingabe mache wirft mir die Laufzeitumgebung drei "unknown software exceptions" (2x 0xc0000090, 1x 0xc0000027). Compiler ist Codegear RAD Studio 2007, OS ist WinXP. Im Debugger erhalte ich an der Stelle wenigstens noch den Hinweis "floating point invalid operation".

    class Mean 
    { 
      unsigned long N; 
      double sum; 
    public: 
      Mean() : N(0), sum(0) {} 
      void add(double value) { sum += value; ++N; } 
      unsigned long get_count() const { return N; } 
      double get_mean() const { return sum / N; } 
      void clear() { sum = 0; N = 0; } 
    }; 
    
    int main()
    {
      // laufen interessanterweise durch, sowohl t1 und t2 sind aber 0.
      int t1 = 0.0 / 0;  // Ergebnis t1 = 0
      int t2 = 0.0 / 0L; // Ergebnis t2 = 0
      int t3 = t1 / t2;  // Software Exception 0xC0000090
    
      Mean mean;
      mean.get_mean(); // Software Exception 0xC0000090
    }
    

    Lustigerweise darf ich im Quelltext durch die Konstante 0 teilen, aber nicht durch Variablen mit Wert 0. Warum läuft das bei dir durch?

    PS:
    Verhalten aktualisiert in C++11? Mein Compiler kann nur C++03.


  • Mod

    Nun, Floating Point Verhalten ist implementation defined. Aber welche Implementierung benutzt denn nicht IEEE floating point? Hast du irgendeine Mathematikoptimierung (beim GCC -ffast-math) angeschaltet? Die Optimieren nämlich so, dass die Sonderwerte nicht auftreten können.

    Integerdivision durch Null ist aber eine floating-point exception, das ist korrekt. Bei t1 und t2 sollte auf der rechten Seite NaN rauskommen, was dann implementation defined in einen int umgewandelt wird. Bei t3 könnte dann alles mögliche rauskommen. In der Regel 1, außer beim ersten Schritt kam 0 für t1 und t2 raus, dann sollte das eine Division durch 0 geben.
    Den Fehler in Zeile 21 kann ich absolut nicht nachvollziehen, außer, dass eben vorher schon eine Exception aufgetreten ist und danach eben nichts mehr stimmt.

    Was gibt denn

    int main()
    {
      Mean mean;
      std::cout << mean.get_mean(); // Software Exception 0xC0000090
    }
    

    ?

    P.S.: Prinzipiell ist das alles implementation defined oder gar undefined, ich beschreibe bloß das Verhalten, das ich kenne.



  • Ich habe mal in den Projekteinstellungen nachgeschaut, da sind "Schnelle Gleitkommaoperationen" aktiviert (Default-Einstellungen). Habe im Moment keine Lust, das jetzt ohne die Options zu prüfen, mach´ ich vielleicht später mal. Sooo wichtig ist das jetzt nun auch wieder nicht. Aber du siehst, dass diese ganze Problematik mit einer expliziten Überprüfung gegen 0 nicht entsteht. Wenn ich nur dann sicherstellen kann, dass mein Programm nur mit bestimmten Projekteinstellungen funktioniert, dann bau ich lieber eine Abfrage mehr ein.

    int main()
    {
      Mean mean;
      std::cout << mean.get_mean(); // Software Exception 0xC0000090
    }
    

    Wirft in get_mean() eine "unknown software exception". Ich habe bei meinen Tests für verschiedene Testläufe einzelne Zeilen auskommentiert und nur das Gesamtergebnis gepostet, das Ergebnis kann also nicht durch UB verfälscht worden sein.


  • Mod

    Aber wie reagierst du auf 0? Mit einer Exception?

    Nein, das ist einfach ein Fehler beim Übersetzen, wenn man schnelle Gleitkommarithmetik aktiviert, obwohl NaN im Programm vorkommen kann. Das ist eine höchst gefährliche Einstellung, da sie, wie du siehst, das Programmverhalten verändert. So etwas darf nie default sein.

    Sonst könntest du dich auch beschweren, dass ein Compileroption wie -Dcout=cin ein Programm durcheinander bringt.



  • SeppJ schrieb:

    Aber wie reagierst du auf 0? Mit einer Exception?

    Nein, ich gebe NaN zurück.

    SeppJ schrieb:

    Nein, das ist einfach ein Fehler beim Übersetzen, wenn man schnelle Gleitkommarithmetik aktiviert, obwohl NaN im Programm vorkommen kann. Das ist eine höchst gefährliche Einstellung, da sie, wie du siehst, das Programmverhalten verändert. So etwas darf nie default sein.

    Sonst könntest du dich auch beschweren, dass ein Compileroption wie -Dcout=cin ein Programm durcheinander bringt.

    Sehen die bei Codegear wohl anders, wenn das deren Default Einstellung ist. Gut, jetzt haben wir beide unseren Standpunkt mit unterschiedlichen Voraussetzungen.


Log in to reply