Komplexer Algorithmus in Klasse?



  • nichttrivialer schrieb:

    Im Prinzip geht es darum, zwischen

    // [...]
    State s2;
    s2.run(...);
    s2.run(...);
    

    und

    // [...]
    State s;
    run(..., s);
    run(..., s);
    

    zu entscheiden.

    In einer Sprache, die Templates und ADL hat, ist ganz klar die zweite Variante zu bevorzugen.

    Warum? Die Unterschiede sind für den Aufrufer nur syntaktisch. Genauso könnte man sagen, in einer Sprache, die OOP hat, ist ganz klar die erste Variante zu bevorzugen. Eine semantische Alternative ist der funktionale Ansatz, weil dadurch Seiteneffekte kontrollierbarer sind:

    State s;
    s = run(..., s); // Übergabe als Kopie oder Move
    s = run(..., s);
    

    nichttrivialer schrieb:

    Gibt ein paar Leute, die sind noch nicht ganz in C++ angekommen (z.B. die von LEMON), aber heutzutage liegt der Konsens bei Variante 2.

    Würde ich nicht sagen. Die Entwickler haben einiges drauf. Hinter den Klassen stecken ziemlich generische und wiederverwendbare Komponenten -- und im Gegensatz zu Boost.Graph haben sie es dennoch geschafft, ein vernünftiges Interface anzubieten. LEMON ist meiner Meinung nach eine recht schöne C++-Bibliothek.



  • Nexus schrieb:

    Warum? Die Unterschiede sind für den Aufrufer nur syntaktisch.

    Nein, die freie Funktion lässt sich überladen.

    namespace FremdesModul {
      class Spezialfall;
      void run(Spezialfall const& m, State& s)
      { /* besserer Algorithmus */ }
    }
    Spezialfall m;
    run(m); // ruft run(Spezialfall const&, State&) durch das run(...)-Template ohne State-Parameter auf
    State s;
    run(m, s); // run(Spezialfall const&, State&)
    

    Gut, das ginge natürlich auch wieder, wenn man in der Klasse eine Indirektion einbaut.

    Aber es gibt hier einen zweiten Punkt:

    Eine semantische Alternative ist der funktionale Ansatz, weil dadurch Seiteneffekte kontrollierbarer sind:

    Mein Ansatz ist der funktionale Ansatz. State ist der Seiteneffekt. Du kannst ohne Seiteneffekt arbeiten (ohne Parameter), ohne Seiteneffekt mit bereits vorhandenem State (dafür braucht es noch eine zusätzliche Funktion

    int run(params, State const& s)
    {
      State copy = s;
      return run(params, copy);
    }
    

    ) oder mit Seiteneffekt.

    Und funktional bedeutet Seiteneffekt kontrollierbar (State ist hier unsere Monade).

    Setz das mal mit Klassen um. Dazu bräuchtest du Verrenkungen wie

    class MyAlgorithm {
      int run(params) { ... }
      int run(params) const { MyAlgorithm copy = *this; return copy.run(params); }
    };
    

    und das für jede Klasse, die run implementiert (es sei denn, man macht einen CRTP-Workaround, was noch hässlicher ist). Die freie Funktion mit ADL braucht das nur einmal.

    Gut, auch das liesse sich in Klassen umsetzen, aber Funktionen sind einfacher umzusetzen (pro Algorithmus bräuchte es 2 freie Funktionen für ADL und die Kurzvariante ohne State), damit und wenn es keinen Grund für Klassen gibt, dann verwende ich lieber Funktionen.

    und im Gegensatz zu Boost.Graph haben sie es dennoch geschafft, ein vernünftiges Interface anzubieten. LEMON ist meiner Meinung nach eine recht schöne C++-Bibliothek.

    Zugegeben, ich kenne LEMON nicht und will nicht schlechtreden, was ich nicht kenne.
    Aber die Transformation a.f(p) -> f(p,a) ist trivial und liesse prinzipiell sich auch mit LEMON umsetzen.

    Ich könnte mir vorstellen, dass die Klassen-Lösung etwas sauberer ist, wenn man den Algorithmus anhalten kann und dann weiter ausführen, es also verschiedene Zwischenstates gibt, die



  • nichttrivialer schrieb:

    Aber die Transformation a.f(p) -> f(p,a) ist trivial und liesse prinzipiell sich auch mit LEMON umsetzen.

    Das schon, aber brächte es einen Mehrwert? Kapselung wäre für die Entwickler mühsamer umzusetzen (entweder alle haben Zugriff auf Interna oder gezielte friend s werden notwendig), und die Schnittstelle ist für den Benutzer möglicherweise weniger intuitiv. Momentan kann man recht praktisch Zwischenzustände kontrollieren (siehe ersten Link). Wenn die Funktionen direkt zur Klasse gehören und man sie ihnen gewaltsam entreissen müsste, finde ich Memberfunktionen schon sinnvoll.

    Ansonsten stimme ich dir aber zu, freie Funktionen können in einigen Szenarien Vorteile bringen.


Anmelden zum Antworten