Ist goto xy wirklich so böse?



  • In diesem Fall easy, da die Lambdas keinen Returnwert haben:

    template <typename Callback>
    bool my_for(size_t start, size_t end, Callback callback)
    {
        for (size_t i = start; i < end; ++i)
            if (!callback(i))
                return false;
        return true;
    }
    

    Lässt sich aber auch allgemein anwenden. In dem Fall dann halt z.B. über nen bool& stop Parameter oder indem man Tuples zurückgibt.

    Oder auch, ganz anderer Ansatz: nich auf alles mit Lambdas draufhauen wo man mit irgendwas draufhauen kann.



  • hustbaer schrieb:

    Wäre interessant wie die Performance davon ist.

    Ja, wär schon interessant, wenn mal jemand sowas ausprobiert ^^
    Und nein, ich glaub nicht, dass die intern longjmp verwenden, das ist denk ich etwas komplizierter.



  • hustbaer schrieb:

    In diesem Fall easy, da die Lambdas keinen Returnwert haben:

    Nee, das is nix, dann muss ich ja immer auch einen Wert in der Schleife zurückgeben auch wenn ich gar nicht breaken will.

    hustbaer schrieb:

    Lässt sich aber auch allgemein anwenden. In dem Fall dann halt z.B. über nen bool& stop Parameter oder indem man Tuples zurückgibt.

    Du meinst z.B. so?

    #include <iostream>
    
    template <typename Callback>
    void my_for(size_t start, size_t end, Callback callback)
    {
    	bool stop = false;
    	for (size_t i = start; i < end; ++i) { callback(i, stop); if (stop) return; }
    }
    
    int main()
    {
    
    	my_for(0, 3, [](size_t i, bool &early_stop_1)
    	{
    		my_for(0, 3, [&i, &early_stop_1](size_t j, bool &early_stop_2)
    		{
    			if (i == 0 && j == 1)
    			{
    				early_stop_1 = true;
    				early_stop_2 = true;
    			}
    			std::cout << i << " " << j << "\n";
    		});
    	});
    }
    

    Klar geht, ist dann halt mehr oder weniger das äquivalent zu Zustands-Variablen wenn man aus einer "normalen" verschachtelten Schleife breaken will. Schön find ichs halt nicht (wie bei "normalen" Schleifen eben auch).

    hustbaer schrieb:

    Oder auch, ganz anderer Ansatz: nich auf alles mit Lambdas draufhauen wo man mit irgendwas draufhauen kann.

    Dafür sind die doch da, gerade bei so kleinem body? Also ich mag Lambdas 🙂



  • happystudent schrieb:

    hustbaer schrieb:

    In diesem Fall easy, da die Lambdas keinen Returnwert haben:

    Nee, das is nix, dann muss ich ja immer auch einen Wert in der Schleife zurückgeben auch wenn ich gar nicht breaken will.

    Nö, musst du nicht.
    Du musst nur für zwei recht unterschiedliche Dinge, nämlich für die Variante die vorzeitigen Abbruch erlaubt und die die keinen vorzeitigen Abbruch erlaubt, zwei unterschiedliche Funktionen machen.

    happystudent schrieb:

    hustbaer schrieb:

    Lässt sich aber auch allgemein anwenden. In dem Fall dann halt z.B. über nen bool& stop Parameter oder indem man Tuples zurückgibt.

    Du meinst z.B. so?
    (snip)

    Klar geht, ist dann halt mehr oder weniger das äquivalent zu Zustands-Variablen wenn man aus einer "normalen" verschachtelten Schleife breaken will. Schön find ichs halt nicht (wie bei "normalen" Schleifen eben auch).

    Ich meins in weniger hässlich und mit nur einer "stop" Variable für beide Schleifen.

    happystudent schrieb:

    hustbaer schrieb:

    Oder auch, ganz anderer Ansatz: nich auf alles mit Lambdas draufhauen wo man mit irgendwas draufhauen kann.

    Dafür sind die doch da, gerade bei so kleinem body? Also ich mag Lambdas 🙂

    Ja, klar. Das schöne neue Werkzeug einfach mal für jede Aufgabe einsetzen, obwohl sich die Aufgabe mit Händen und Füssen dagegen wehrt, ist immer volle schlau.

    ps:

    happystudent schrieb:

    my_for(0, 3, [](size_t i)
        {
            my_for(0, 3, [&i](size_t j)
            {
    

    &i
    *facepalm*



  • hustbaer schrieb:

    Ja, klar. Das schöne neue Werkzeug einfach mal für jede Aufgabe einsetzen, obwohl sich die Aufgabe mit Händen und Füssen dagegen wehrt, ist immer volle schlau.

    Weiß ja nicht wo du rausliest dass ich das für jede Aufgabe einsetze. 😕

    Nur so: Das war ein Minimalbeispiel. Ich ersetze nicht jede for-Schleife durch ein Lambda, falls du das dachtest.

    Gerade beim Erstellen einer eigenen for Schleife wehrt sich die Aufgabe aber nicht "mit Händen und Füßen", sondern ist geradezu prädestiniert dafür, z.B. concurrency::parallel_for oder std::for_each .

    hustbaer schrieb:

    &i
    *facepalm*

    Äh, was ist daran auszusetzen? Wenn ich in der inneren Schleife auf i zugreifen können will muss ich es auch capturen.

    Und da ich i , wie in einer normalen inneren for-Schleife ändern können will, capture by reference.

    Wenn du hier schon facepalmst dann bitte mit Begründung, bin gespannt 🙂



  • happystudent schrieb:

    Gerade beim Erstellen einer eigenen for Schleife wehrt sich die Aufgabe aber nicht "mit Händen und Füßen", sondern ist geradezu prädestiniert dafür, z.B. concurrency::parallel_for oder std::for_each .

    Naja sie wehrt sich zumindest ein bisschen, wenn du die Schleife vorzeitig abbrechen willst. Und std::for_each ist IMO auch ein ganz schlechtes Beispiel, weil in C++11 mMn. völlig redundant. parallel_for macht natürlich Sinn.

    happystudent schrieb:

    Und da ich i , wie in einer normalen inneren for-Schleife ändern können will, capture by reference.

    Wenn du hier schon facepalmst dann bitte mit Begründung, bin gespannt 🙂

    Die Möglichkeit i ändern zu können macht in deinem Beispiel überhaupt keinen Sinn. Schlimmer noch: wenn man so etwas wie dein "my_for" bastelt, dann will man normalerweise gar nicht dass der Funktor die Laufvariable ändern kann.

    Man *kann* das machen, wenn es denn wirklich Sinn macht, und natürlich *kann* es Sinn machen. Dadurch werden aber Dinge die eigentlich Implementierungdetails der Funktion sind zum Contract erhoben - auch wenn man sich darüber vielleicht nicht im klaren ist und es nicht dokumentiert.

    Nämlich z.B. wie die Laufbedingung ist. Also ob i != end oder i < end . Oder ob es evtl. sogar i >= begin && i < end ist - was nötig wäre wenn man dem Funktor auch erlauben möchte abzubrechen indem man i < begin setzt.
    Weiters geht ein recht angenehmer Nebeneffekt der Funktion verloren, nämlich dass man die Laufvariable eben gerade nicht ändern kann. Durch diesen komplizierteren Contract schränkst du nicht nur die Implementierung ein (schlechtere Performance wegen beidseitigem Range-Check) sondern machst das ganze auch aufwendiger zu verstehen. Also aufwendiger richtig anzuwenden, und ganz wichtig: auch aufwendiger zu lesen, da der Leser sich eben nicht mehr darauf verlassen kann dass die Schleife einfach immer genau 1x von vorn bis hinten durchpfeifft ohne was zu überspringen oder zu wiederholen.

    Und da aus dem Beispiel kein Grund erkennbar ist wieso man i ändern wollen würde (und das Beispiel i auch gar nicht ändert), ist dieses Detail in diesem Beispiel ein *facepalm*. Und nochmal mehr, weil es nichtmal gehen würde - der äussere Funktor nimmt i ja by-value.

    Alles in allem bleibt also der Eindruck dass hier entweder
    - grundsätzlich gerne by-reference gecaptured wird, was definitiv einen dicken *double-facepalm* verdient oder
    - ein Werkzeug wegen "könnte ja mal wer machen wollen" unnötig flexibel und damit auch unnötig komplex gemacht wurde - was zumindest einen einfachen *facepalm* verdient.

    happystudent schrieb:

    hustbaer schrieb:

    Ja, klar. Das schöne neue Werkzeug einfach mal für jede Aufgabe einsetzen, obwohl sich die Aufgabe mit Händen und Füssen dagegen wehrt, ist immer volle schlau.

    Weiß ja nicht wo du rausliest dass ich das für jede Aufgabe einsetze. 😕

    Ja, damit hast du Recht. Sorry!



  • hustbaer schrieb:

    Und std::for_each ist IMO auch ein ganz schlechtes Beispiel, weil in C++11 mMn. völlig redundant.

    Gab es eigentlich std::for_each und range based for schon immer zusammen? Weil wenn ja, macht das wirklich irgendwie wenig Sinn 😃

    hustbaer schrieb:

    Die Möglichkeit i ändern zu können macht in deinem Beispiel überhaupt keinen Sinn.

    Ok, jetzt verstehe ich was du meinst. Das stimmt natürlich, wollte für das Beispiel halt möglichst nahe an eine "richtige" for-Schleife rankommen - da kann man ja ohne weiteres auch i ändern. In der Realität würde ich das aber natürlich nicht capturen, wenn nicht notwendig.

    hustbaer schrieb:

    Ja, damit hast du Recht. Sorry!

    Kein Problem, aber es stimmt natürlich auch dass man das sparsam einsetzen sollte... Bei zwei Schleifen gehts noch, aber mehr würde ich dann wahrscheinlich auch nur ungern verwenden.



  • happystudent schrieb:

    hustbaer schrieb:

    Und std::for_each ist IMO auch ein ganz schlechtes Beispiel, weil in C++11 mMn. völlig redundant.

    Gab es eigentlich std::for_each und range based for schon immer zusammen? Weil wenn ja, macht das wirklich irgendwie wenig Sinn 😃

    Nein, std::for_each ist C++98 und range based for ist C++11. Aber range based for und Lambdas sind beide C++11. Es gibt also keinen Grund (zumindest keinen der mir einfällt) std::for_each mit Lambdas zu verwenden. Also ausser partiellem C++11 Support oder sowas.

    happystudent schrieb:

    hustbaer schrieb:

    Die Möglichkeit i ändern zu können macht in deinem Beispiel überhaupt keinen Sinn.

    Ok, jetzt verstehe ich was du meinst. Das stimmt natürlich, wollte für das Beispiel halt möglichst nahe an eine "richtige" for-Schleife rankommen - da kann man ja ohne weiteres auch i ändern.

    Ja, sowas, also dass man versucht etwas anderes möglichst 1:1 nachzubilden, kann einem schnell mal passieren. Ist aber oft gar nicht so gut, weil man damit eben oft etwas schafft was unnötig flexibel und damit unnötig kompliziert ist. In einem Beispiel natürlich nicht tragisch, aber wenn man so production Code entwirft finde ich das gar nicht so gut.


  • Mod

    hustbaer schrieb:

    Es gibt also keinen Grund (zumindest keinen der mir einfällt) std::for_each mit Lambdas zu verwenden.

    Iteratoren-Paar statt Range? Wir haben schließlich immer noch kein iterator_pair in der Standardbibliothek...



  • OK, das ist ein Grund der mir nicht eingefallen ist 🙂
    Wobei, wenn man es öfter braucht, ... die paar Zeilen die man für ne minimale iterator_pair Implementierung braucht... das könnte sich schon auszahlen.
    Und ich glaube ich würde das lieber machen als nur deswegen std::for_each zu verwenden. Bzw. ich würde boost::make_iterator_range() verwenden, aber angenommen ich hätte keine Boost...


Anmelden zum Antworten