STL Datencontainer, Iteratoren und die dicken Köpfe...



  • "Ich führe die Operation auf einem Objekt aus"
    "Ich führe die Operation auf jedem von einer Reihe von Objekten aus"

    Mit for_each lassen sich "die Operation" und "führe auf jedem von einer Reihe von Objekten aus" hervorragend trennen 😉



  • Wenn Du eh schon Boost verwendest, dann rate ich Dir zu BOOST_FOREACH:

    #include <boost/foreach.hpp>
    #define foreach BOOST_FOREACH
    
    // …
    
    typedef map<boost::shared_ptr<A>, boost::shared_ptr<B> >::const_iterator citer_t;
    
    foreach(citer_t i, data)
        tuwas(*i);
    


  • Tyrdal schrieb:

    for_each ist blöd, weil man da noch nen Funktor braucht und der Code deshalb an ganz anderer Stelle steht. (Außer man nimmt boost::lambda, dann wirds aber auch nicht grade übersichtlicher als die Schleife.)

    Ja, fast richtig. Allerdings gibt's für die Implementierung eines simplen objekt->tuwas(arg) viele, tolle, generische Bausteine.

    Dass Boost.Lambda nicht übersichtlicher ist, möchte ich jedoch bezweifeln. Vor allem ist es deutlicher.



  • Danke für die vielen hilfreichen Tipps. BOOST_FOREACH scheint mir für meinen Fall der geeignetste Weg zu sein. 😃



  • Das du die Funktion auch von anderen Stellen aus benutzen kannst? Das du die Implementierung unabhängig davon austauschen kannst?

    Kommt bei mir aber extrem selten vor, daß ich Code, der in solchen Schleifen steht, irgendwo wiederverwenden kann.

    Schon mal was von Modularität gehört?

    Schon, ist aber in dem Fall unnötig, s.o.. Allein kann ich mit meiner Meinung ja nicht dastehen, sonst gäbe es ja das BOOST_FOREACH Makro ja nicht. Das paßt besser zu meinem Stil.



  • Tyrdal schrieb:

    Das du die Funktion auch von anderen Stellen aus benutzen kannst? Das du die Implementierung unabhängig davon austauschen kannst?

    Kommt bei mir aber extrem selten vor, daß ich Code, der in solchen Schleifen steht, irgendwo wiederverwenden kann.

    Hmm. Finde ich komisch, denn eigentlich lassen sich die meisten Aufgaben, die in Schleifen ausgeführt werden, recht gut auf einige Basisoperationen reduzieren, die man dann mit Map- und Filter-Operationen verknüpfen kann (wobei ein Map eine Funktion T -> T ist, während ein Filter eine Funktion T -> bool ist, die Elemente aussortiert). Andere Programmiersprachen (Haskell …) basieren auf dieser Annahme und sind da recht erfolgreich mit.



  • Dann ist das der Grund warum ich mich bisher nicht mit funktionaler Programmierung anfreunden konnte. 🙂
    Aber mal im Ernst, gib mal ein wiederwendbares Beispiel an. Vielleicht lern ich ja dann noch was.



  • Konrad Rudolph schrieb:

    (wobei ein Map eine Funktion T -> T ist, während ein Filter eine Funktion T -> bool ist, die Elemente aussortiert).

    Wobei das natürlich nur die Signaturen der Argumentfunktionen sind.

    map :: (\T -> T), [T] -> [T]
    
    filter :: (\T -> bool), [T] -> [T]
    
    foldl :: (\T, T -> T), T, [T] -> T
    


  • Tyrdal schrieb:

    Dann ist das der Grund warum ich mich bisher nicht mit funktionaler Programmierung anfreunden konnte. 🙂
    Aber mal im Ernst, gib mal ein wiederwendbares Beispiel an. Vielleicht lern ich ja dann noch was.

    http://www.joelonsoftware.com/items/2006/08/01.html



  • finix schrieb:

    Tyrdal schrieb:

    Dann ist das der Grund warum ich mich bisher nicht mit funktionaler Programmierung anfreunden konnte. 🙂
    Aber mal im Ernst, gib mal ein wiederwendbares Beispiel an. Vielleicht lern ich ja dann noch was.

    http://www.joelonsoftware.com/items/2006/08/01.html

    Grmpf, vielen Dank! Ich wollte vorhin schon einen Link auf MapReduce von Google posten, aber ich wusste den genauen Namen nicht mehr (ich habe mich nur an die Haskell-Terminologie erinnert, also „reduce“ = „fold“) und habe nichts gefunden.



  • Java required you to create a whole object with a single method called a functor if you wanted to treat a function like a first class object. Combine that with the fact that many OO languages want you to create a whole file for each class, and it gets really klunky fast. If your programming language requires you to use functors, you're not getting all the benefits of a modern programming environment. See if you can get some of your money back.

    So ganz kommt die Seite also auch nicht an meinem Argument vorbei. Wobei der Hinweis auf die einfachere Parallelisierung ein echtes Argument ist. Aber das kann man ja im Bedarfsfall so machen und für den Rest BOOST_FOREACH nehmen.

    Ansonsten danke ich für die Begegnung mit den Muppets. Ich liebe sie.



  • Tyrdal schrieb:

    Java required you to create a whole object with a single method called a functor if you wanted to treat a function like a first class object. Combine that with the fact that many OO languages want you to create a whole file for each class, and it gets really klunky fast. If your programming language requires you to use functors, you're not getting all the benefits of a modern programming environment. See if you can get some of your money back.

    So ganz kommt die Seite also auch nicht an meinem Argument vorbei.

    Na ja, dafür gibt es in C++ ja Boost.Lambda und ähnliches. Nur weil es Java nicht kann, heißt es noch lange nicht, dass da andere Sprachen nicht weiter sind.



  • Schon richtig, aber ich hab ja in einem vorherigen Post geschrieben, daß ich die Sache mit boost::Lambda nicht übersichtlicher find. Und außer für Parallelisierung seh ich da jetzt auf der Seite keinen Vorteil, der die schlechtere Lesbarkeit (in C++)rechtfertigen würde. Naja ist wohl Geschmackssache.



  • @finix, Konrad Rudolph
    Also irgendwie verstehe ich euren Punkt nicht. Klar, map/reduce-Konstrukte sind Hammer (sehe ich auch so), aber was hat das mit dem wirklich nervigen "Code woanders hin schreiben müssen" zu tun? Wer map/reduce kann, kann normalerweise auch anonyme Funktionen und deshalb schreibt man i.d.R. (so wie auch Joel in seinem Artikel):

    map( function(x){x.print();}, a );
    

    statt

    // irgendwo an einer völlig anderen Stelle
    struct PrintAdapter {
       template<class T>
       void operator()(const T& x) const {
         x.print();
       }
    };
    // und dann wiederum an einer anderen Stelle
    map( PrintAdapter(), a );
    

    Die Frage: "Wo bitte ist der Vorteil, dass der Code woanders steht?" bleibt imo völlig berechtigt und hat nichts mit map/foreach/... vs. for-Schleifen zu tun. Anders: for-Schleifen sind nicht deshalb mist weil der Code, der pro Iteration ausgeführt wird im Body der Schleife steht, sondern weil das Iterieren nicht abstrahiert ist.



  • HumeSikkins schrieb:

    Die Frage: "Wo bitte ist der Vorteil, dass der Code woanders steht?" bleibt imo völlig berechtigt und hat nichts mit map/foreach/... vs. for-Schleifen zu tun.

    Du vergisst, dass *ich* derjenige war, der BOOST_FOREACH in diesem Thread vorschlug. Ich bin auf Deiner Seite, Du musst mich nicht überzeugen. Trotzdem sind auch die in der Schleife vollführten Operationen oft dieselben, parametrisierbaren Algorithmen (z.B. beim Filtern ein less_than-Vergleich oder beim Aufeinanderfalten (Reduzieren) eine einfache Addition), und in solchen Fällen lohnt es sich eben, wiederverwendbare Funktoren zu schreiben.



  • @Konrad Rudolph
    Ok. Dann habe ich da wohl was mißverstanden.
    Wie auch immer:

    Trotzdem sind auch die in der Schleife vollführten Operationen oft dieselben, parametrisierbaren Algorithmen (z.B. beim Filtern ein less_than-Vergleich oder beim Aufeinanderfalten (Reduzieren) eine einfache Addition), und in solchen Fällen lohnt es sich eben, wiederverwendbare Funktoren zu schreiben.

    Es lohnt sich eine Abstraktion (Funktion) für "Filtern" und "Aufeinanderfalten" zu schreiben. Da stimme ich zu und deshalb bin ich ja auch ein großer Freund von einfachen Standardalgos wie std::for_each oder std::accumulate. Es lohnt sich aber imo keinesfalls eine Funktion für "less_than"-Vergleich oder "einfache Addition" zu schreiben und deshalb mag ich ja auch die einfachen Standardalgos wie std::for_each und std::accumulate nicht (der Widerspruch ist beabsichtigt). Denn ohne boost::lambda o.Ä. schreibt man ständig irgendwelche völlig trivialen Funktoren oder aber unglaublich generische, die dann über 5 Binder zusammengeschustert werden und damit am Ende nur eins produzieren: Unverständnis beim Leser.



  • Dann hoffen wir mal, daß mit C++0x(09?!) und den "Variadic Templates" das bind-Template dann übersichtlicher und einfacher zu verwenden sein wird. Und Lambda-Funktionalität soll ja auch angeboten werden (die Platzhalter _1, _2, _3, ... sollen dafür wohl genutzt werden können).
    Obwohl ich selber ein Anhänger der "Funktionalen Programmierung" bin, verwende ich die vordefinierten Standard-Algos bisher leider weitaus weniger (als ich es eigentlich sollte -) - so wie HumeSikkins es ja auch geschrieben hat.
    Umgekehrt hasse ich aber auch immer die selbstgeschriebenen Schleifen...

    C++ ist halt eine wunderbare Multiparadigmen-Sprache -)


Anmelden zum Antworten