Optimieren, need help



  • 0xdeadbeef schrieb:

    volkard schrieb:

    klar, am anfang nimmt man irgendwas, selbst wenn es ein vector mal ist, der aufgaben einer map übernimmt. genau auswählen kann man ja später noch, ohne was umzuschmeißen. oo ist toll.

    Hm. Eigentlich ist das nun grad mehr ein Merkmal der generischen Programmierung. Gut, wenn dus richtig anfängst, musst du auch in einem OO-Programm nur ein paar typedefs umhängen, aber...
    Naja, da kommt es wirklich auf den Anwendungsbereich an.

    Quark. Das mit dem Wegkapseln und somit einfach ändern zu können ohne den Rest anzupassen ist OO. Generische Programmierung ist wenn Code generiert wird und hat hiermit garnichts zu tun!



  • Aber mitnichten. Das einfache drop-in-replacement irgendwelcher Kernbauteile ist generisch durch und durch - darum macht man ja diesen ganzen Aufriss mit den Konzepten. Du erwartest nicht mal mehr eine bestimmte Art Datentyp, sondern einfach ein bestimmtes Interface (sog. "Konzept"), und was dir nachher konkret untergejubelt wird, ist dir ziemlich egal.

    Der Unterschied zur OOP ist, dass du nachher flexibler bist - im generischen kannst du nachher da, wo du den Kram benutzt, entscheiden, was du benutzen willst. In der OOP musst du das zentral in der Klasse festlegen.

    @Mastah: Meiner Erfahrung nach ist das eher selten der Fall, vor allem, wenn du die Datenstrukturen genau auf ein Problem anpasst, so dass du sie nachher für so gut wie nichts anderes benutzen kannst. Das heißt aber natürlich nicht, dass das für alle Leute so gilt wie für mich.



  • class Foo {
    public:
       void showContent()
       {
          copy (container.begin(), container.end(), ostream_iterator<int>(cout, " "));
       }
       ...
    private:
       vector<int> container;
    };
    
    int main ()
    {
       Foo foo;
       .. // ein par methoden (die nicht gezeigt wurden), damit auch inhalt da ist
    
       foo.showContent();
    }
    

    und jetzt merke ich, dass vector kacke ist.

    class Foo {
    public:
       void showContent()
       {
          copy (container.begin(), container.end(), ostream_iterator<int>(cout, " "));
       }
       ...
    private:
       list<int> container;
    };
    

    main kann unverändert bleiben, auf Grund der Kapselung[b]. [b]Kapselung ist ein Teil der OOP. Generizität hatte damit nichts zu tun.

    Du erwartest nicht mal mehr eine bestimmte Art Datentyp, sondern einfach ein bestimmtes Interface (sog. "Konzept"), und was dir nachher konkret untergejubelt wird, ist dir ziemlich egal.

    Schon mal in einer nicht statisch typisierten OO-Spracheprogrammiert? Genau das kannste da machen. Du beschreibst also die OOP und behauptest dann das sei Generizität?
    😕



  • ich glaub, er meinte was mit templates 😉



  • *Seufz*.

    #include <algorithm>
    #include <iostream>
    #include <iterator>
    #include <list>
    #include <vector>
    
    template <typename val_t = int,
              template<typename v_t> class container_t = std::list>
    class Foo {
    public:
      typedef val_t value_type;
      typedef container_t<val_t> container_type;
    
      void showContent()
      {
        std::copy(container.begin(),
                  container.end(),
                  std::ostream_iterator<value_type>(std::cout, " "));
      }
    
      //...
    
    private:
      container_type container;
    };
    
    int main ()
    {
      Foo<double, std::vector> foo;
    
      //...
    
      foo.showContent();
    }
    

    See what I mean?



  • Das ist die dämlichste Idee, die ich seit langem gesehen habe. Du rennst total am Problem vorbei. Natürlich kann man das machen, was du da zeigst, man kann sich aber auch ein Loch ins Knie boren und warme Milch reinkippen.

    Ich will nicht wählen müssen, welcher Container verwendet wird. ICh will das das feststeht.

    Ich will einfach schreiben

    Foo foo;
    
    foo. neMethode ();
    

    Ich will niemals mit verschiedenen Containern arbeiten. Foo hat eine einzige ganz bestimmte Aufgabe. Am Anfang war mir allerdings noch nicht klar, ob std::vector der richtige Container zum lösen des Problems innerhalb von Foo war. Trotzdem habe ich einfach mal einen std::vector verwendet.
    Nun kann ich und alle meine Kollegen Foo bereits verwenden, da es ja schon läuft. Dann beim ausbauen, refaktorisieren, optimieren oder sonstwas fällt mir auf, dass std::vector eine schlechte Wahl war und ersetze ihn durch std::list und passe gegebenen falls ein paar der Methoden von Foo an.

    Bei deinem Vorschlag müsste ich und alle meine Kollegen den gesammten Code überarbeiten überall, wo bisher Foo stand muss jetzt Foo<int, std::list> hin. Das ist unmengen unnütze Arbeit und zudem total hässlich und unübersichtlich.



  • @Helium: Schonmal std::stack angeschaut?

    MfG Jester



  • stack ist ein Container, wie quque auch. Er kann mit einem anderen Container parametrisiert werden.

    Das ist eine ganz andere Situation. Bei stack geht es um eine allgemeine Struktur, die ich an die jeweilige Situation anpassen möchte. Bei meinem "Foo" geht es um ein Teil des Projekts, der in der Form überall im Projekt Verwendung findet.
    Wenn ich nun merke, das die Struktur in der Form viel zu langsam oder viel zu speicherraubend ist, dann will ich sie einfach ändern können. Dadurch ist mein Projekt dann dementsprechend schneller oder weniger speichrraubend. Und ich will nur eine stelle ändern müssen, nicht das ganze Projekt.



  • Wenn du mal genau hinkuckst, du musst nicht wählen, welchen Container du benutzen willst, du kannst auch einfach den Default-Wert (in diesem Fall std::list) nehmen.



  • Helium schrieb:

    Bei meinem "Foo" geht es um ein Teil des Projekts, der in der Form überall im Projekt Verwendung findet.

    Wußte ich nicht. Dachte immer Foo sei "hier beliebiges hindenken".



  • es ist eh selten eine gute idee. vorher einen ganz unpassenden container zu wählen. am besten ist, man wählt vorher einen, der schon recht genau passt. einen, dessen schnittstellen paßt.
    und den kann man später austauschen, weil man von anfang an damit rechnete, ihn auszutauschen. insbesondere meine ich damit, daß man zwar ne std::map mal benutzt, weil's gerade so einfach ist, aber von der eigenschaft, daß sie beim durchiterieren ihre keys sortiert darbringt, keinen gebrauch macht.
    und schwupps, hat man die möglichkeit, viele andere datenstrukturen zu nehmen. hashtables natürlich. skip-lists. und vor allem die ganen kombinationen, damit beginnt der spaß ja erst richtig.



  • Ja, ja, schon klar - aber wenn man den Kram nicht konkretisieren will, gibts ja noch den Default-Wert. Aber es kann schon sinnvoll sein, den Datentyp zu parametrisieren - je nach Gelegenheit kann ein ganz anderer Datentyp vorteilhaft sein. Stell dir vor, du brauchst Foo an zwei Stellen des Programms. Das eine mal kannst du vorher feststellen, wie viele Werte du kriegst, das andere Mal hast du nicht die geringste Ahnung. Das eine Mal hättest du gerne nen Vektor, das andere mal nen Baum (auch wenn volkard mich dafür jetzt wahrscheinlich wieder verbal verprügeln wird 😉 ). Und da bist du mit reiner OOP ziemlich aufgeschmissen.



  • Solche Datenstrukturen wirst du aber selten anfinden, es sei denn es geht gerade (wie bei std::stack und den anderen STL-Containern) darum ganz allgemein zu sein.

    class Base {
    public:
       virtual void add (Foo const & foo) = 0;
    };
    
    class Impl1 : public Base {
    public:
       void add (Foo const & foo)
       {
          container.push_back (foo);
       }
    private:
       std::vector<Foo> container;
    };
    
    class Impl2 : public Base {
    public:
       void add (Foo const & foo)
       {
          container.insert (foo);
       }
    private:
       std::set<Foo> container;
    };
    

    Du siehst, reine OO. und dennnoch kann ich in verschiedenen Situationen verschiedene Ddatenstrukturen verwenden. Und ich habe dieses bekloppte Beispiel nur geschreiben, um dich zu ärgern. :p



  • 0xdeadbeef schrieb:

    Das eine mal kannst du vorher feststellen, wie viele Werte du kriegst, das andere Mal hast du nicht die geringste Ahnung. Das eine Mal hättest du gerne nen Vektor, das andere mal nen Baum (auch wenn volkard mich dafür jetzt wahrscheinlich wieder verbal verprügeln wird 😉 ). Und da bist du mit reiner OOP ziemlich aufgeschmissen.

    erstmal die verlanten prügel: *patsch*

    kann nicht nachvollziehen, warum die anzahl den wunsch nach baum erzeugt. ne hashtable kann genauso wachsen wie ein baum. die mit verkettung im überlaufbereich (also die inzwischen normalen) können auch in grenzen recht nett schrumpfen.

    ich halte nix davon, fette universalschnittstellen zu bauen, wo mal ein baum, mal ne hashtable, mal ein array und mal ne liste drunterliegen und alle strukturen bietet sort, find, remove, insert und iteratoren an. hätte man sowas, könnte man natürlich fein drauflosprogrammieren und erst am ende vor der haustür des kunden noch schnell entscheiden, welche datenstruturen man nimmt. aber vielleicht könnt ihr mich ja auch her von eurem modernen stil überzeugen.



  • Warst du nicht derjenige, der meinte, man sollte sich je nach Problem eigene Datenstrukturen bauen, um die Performance zu steigern? Hier hast du die perfekte Möglichkeit, dir je nach Problem eine auszusuchen und die Interfaces quasi vom Speichermanagement abzukapseln - und das alles ohne overhead. Ravioli-Code par excellence.

    @Helium: Ich finde das ziemlich umständlich, und vom Laufzeitverhalten her ist es grausam. virtual void ad...bah. Nehmen wir (um dem Beispiel mal ein bisschen Sinn einzuhauchen) an, std::set hätte tatsächlich keine push_back-Methode, dann schreib ich trotzdem immer noch lieber

    template <template<typename t> class container_t> class Foo {
    private:
      cointainer_t<Bar> container;
    
    public:
      inline void add(const Bar &bar) { container.push_back(bar); }
    };
    
    template<> void Foo<std::set>::add(Bar &bar) { container.insert(bar); }
    

Anmelden zum Antworten