Lukas.: Gibt es einen Sinn für Anweisungen in if-Bedingiungen?

Hallo,

mit

if (Variable = false) {
// mach das
} else {
// mach dies
}

springt man zweifellos in die "else-Bedingung".

Gibt es irgendeine Situation, wo ein solches Konstrukt sinnvoll angewendet wird (bzw. einen sinnvollen Nutzen ergibt) oder ist das einfach "logisch aber nutzlos"?

L.

  1. @@Lukas.

    Gibt es irgendeine Situation, wo ein solches Konstrukt sinnvoll angewendet wird

    Es gibt wohl keine Situation, wo das Konstrukt if (variable = irgendwas) sinnvoll angewendet wird, weil die Verwechsungsgefahr mit if (variable == irgendwas) zu groß ist.

    LLAP 🖖

    --
    “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
    1. Hallo Gunnar Bittersmann,

      Es gibt wohl keine Situation, wo das Konstrukt if (variable = irgendwas) sinnvoll angewendet wird, weil die Verwechsungsgefahr mit if (variable == irgendwas) zu groß ist.

      bei while ist es aber üblich. Etwa, wenn man eine unbekannte Anzahl Datensätze ausgeben möchte.

      Bis demnächst
      Matthias

      --
      Rosen sind rot.
      1. Hi Matthias,

        bei while ist es aber üblich. Etwa, wenn man eine unbekannte Anzahl Datensätze ausgeben möchte.

        Aber dort würde doch dann ein

        while (variable) {...
        

        dasselbe ergeben oder meintest Du genau das?

        L.

        1. Hallo Lukas.,

          $blocks = $db -> prepare("SELECT * FROM table");
          $blocks -> execute();
          while ($block = $blocks -> fetch(PDO::FETCH_ASSOC)) {
            // in $block steckt jetzt solange ein Datensatz, wie die Zuweisung funktioniert
          }
          

          Bis demnächst
          Matthias

          --
          Rosen sind rot.
          1. // in $block steckt jetzt solange ein Datensatz, wie die Zuweisung funktioniert

            Hi Matthias,

            eben das meinte ich auch.

            L.

            1. Tach!

              // in $block steckt jetzt solange ein Datensatz, wie die Zuweisung funktioniert

              Hi Matthias,

              eben das meinte ich auch.

              Nicht ganz, denn wenn du nur die Variable auswertest, muss die Anweisung zum Befüllen derselben anderswo notiert sein. Zum einen vor dem Schleifenkörper und dann noch einmal innen drin, am Ende.

              dedlfix.

            2. Hallo Lukas.,

              eben das meinte ich auch.

              Ich denke, nicht.

              Mit

              $blocks = $db -> prepare("SELECT * FROM table");
              $blocks -> execute();
              while ($blocks -> fetch(PDO::FETCH_ASSOC)) {
                // 
              }
              

              erhältst du nur die Information, dass es einen Datensatz gibt. Die Daten, die er enthält, musst du dir jetzt noch anders wie holen.

              Bis demnächst
              Matthias

              --
              Rosen sind rot.
              1. erhältst du nur die Information, dass es einen Datensatz gibt. Die Daten, die er enthält, musst du dir jetzt noch anders wie holen.

                Ok.

                L.

    2. Hallo @@Gunnar Bittersmann,

      Es gibt wohl keine Situation, wo das Konstrukt if (variable = irgendwas) sinnvoll angewendet wird, weil die Verwechsungsgefahr mit if (variable == irgendwas) zu groß ist.

      wenn irgendwas eine Funktion ist und ich variable im Anweisungsblock nutzen möchte, dann schon, z.B.

      if (variable = boolescheFunktion()) {
          macheEtwasMit(variable);
          
          // …
      }
      

      Viele Grüße
      Robert

      1. Hi Robert,

        Es gibt wohl keine Situation, wo das Konstrukt if (variable = irgendwas) sinnvoll angewendet wird, weil die Verwechsungsgefahr mit if (variable == irgendwas) zu groß ist.

        wenn irgendwas eine Funktion ist und ich variable im Anweisungsblock nutzen möchte, dann schon, z.B.

        if (variable = boolescheFunktion()) {
            macheEtwasMit(variable);
            
            // …
        }
        

        Und wo soll dann der Unterschied zu

             if (variable == boolescheFunktion()) {
                macheEtwasMit(variable);
                
                // …
              }
        

        sein, außer, dass dann das true-Ergebnis im if-Bereich steht und das false-Ergebnis im else-Bereich?

        L.

        1. Hallo Lukas,

          hier wird variable

          1. mit dem Ergebnis von boolescheFunktion() initialisiert
          2. und falls es (sich wie) ein wahrer Wert ist (verhält), wird der Block ausgeführt.
          if (variable = boolescheFunktion()) {
              macheEtwasMit(variable);
              
              // …
          }
          

          Und wo soll dann der Unterschied zu

               if (variable == boolescheFunktion()) {
                  macheEtwasMit(variable);
                  
                  // …
                }
          

          sein, außer, dass dann das true-Ergebnis im if-Bereich steht und das false-Ergebnis im else-Bereich?

          In deinem Beispiel findet keine Zuweisung statt, sondern nur ein Vergleich. OK, vielleicht hätte ich bei C-ähnlicher Syntax der Funktion auch einen besseren Namen geben sollen, wie z.B. intFunktion(), dann wäre es gleich klar. In C (und verwandten Sprachen) gibt es Typen, die als boolesch verglichen werden können, z.B. int oder Pointer (in C++ mit der Überladung des operator bool sogar noch viel mehr).

          Viele Grüße
          Robert

      2. @@Robert B.

        Es gibt wohl keine Situation, wo das Konstrukt if (variable = irgendwas) sinnvoll angewendet wird, weil die Verwechsungsgefahr mit if (variable == irgendwas) zu groß ist.

        wenn irgendwas eine Funktion ist und ich variable im Anweisungsblock nutzen möchte, dann schon, z.B.

        if (variable = boolescheFunktion()) {
            macheEtwasMit(variable);
            
            // …
        }
        

        Nein. Die Begründung gab ich schon.

        Außerdem ist variable überflüssig, da logisch ist (no pun intended), dass deren Wert true ist.

         if (boolescheFunktion()) {
             macheEtwasMit(true);
             
             // …
         }
        

        LLAP 🖖

        --
        “When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory
  2. Tach!

    if (Variable = false) {
    // mach das
    } else {
    // mach dies
    }
    

    springt man zweifellos in die "else-Bedingung".

    Gibt es irgendeine Situation, wo ein solches Konstrukt sinnvoll angewendet wird (bzw. einen sinnvollen Nutzen ergibt) oder ist das einfach "logisch aber nutzlos"?

    Mit dem false als Literal ist das zweifelsohne sinnfrei, obwohl es auch dafür einen Anwendungsfall geben kann. Aber mit einem Funktionsaufruf, der ein boolesch auswertbares Ergebnis bringt, mit dem man dann auch noch weiterarbeiten möchte, ist ein solches Konstrukt genauso sinnvoll wie den bereits erwähnten Anwendungsfall mit dem while. Man kann sogar denselben Fall mit if verwenden, wenn die Ergebnismenge der Datenbankabfrage nur einen einzelnen Datensatz oder keinen liefern kann. Dann brauchte zwar die Auswertung, ob oder ob nicht, aber keine Schleife zum Abfragen.

    Der "sinnfreie" Anwendungsfall wäre, dass man zunächst ein sinnvolles Konstrukt hatte, sich aber die Anforderungen geändert haben, man aber wegen $grund kein vollständiges Refactoring machen möchte, bei dem nur noch der else-Zweig übrig bleibt. Ein $grund wäre zum Beispiel: "vielleicht brauchen wir das ja später doch noch mal".

    dedlfix.

    1. Hi dedlfix,

      2 bzw. 3 gute Ideen, dank Dir.

      L.

  3. Wie schon geschrieben - mit "= false" macht es keinen Sinn, es muss schon etwas sein, dessen Wert sich pro ändern kann. Und dann ist

       if (variable = any_expression) {
          // action for true-ish value
       } else {
          // action for falsy value
       }
    
       while (row = get_next_row()) {
          // row processing
       }
    
       // oder ganz "geschickt":
       for (var db = open_database(); row = get_next_row(db); ) {
          // row processing
       }
    
    

    durchaus eine sehr typische und häufige Formulierung für Sprachen wie C, Java, JavaScript oder PHP.

    Warum? Weil Programmierer faul sind, weil die alten Optimizer noch nicht so viel taugten, und man auf diese Weise 3 Bytes Source- und Object-Code sparen konnte. In einer Schleife wird es nochmal eine Nummer schicker.

    LEIDER!!!

    Was sind die Fallstricke?

    • Man kann = und == verwechseln
    • Man setzt implizit voraus, dass die Sprache auch nicht-boolesche Werte in Bedingungen zulässt und sie nach gewissen Regeln als truthy oder falsy interpretiert.

    Es gibt durchaus Sprachen, die diese Konstrukte schlichtweg verbieten, z.B. C#. Nicht weil die Sprachentwickler zu blöd waren, sondern weil dieses Konstrukt fehlerträchtig ist. Warum tut es so weh, folgendes zu schreiben:

       var row = get_next_row();
       while (row !== null) {
          // process row
          row = get_next_row();
       }
    

    Oder wenn man schon kompakt sein will:

       while ((row = get_next_row()) !== null) {
          // process row
       }
    

    Das ist eh das, was der Compiler unter der Haube täte.

    Rolf

    1. Warum tut es so weh, folgendes zu schreiben:

         var row = get_next_row();
         while (row !== null) {
            // process row
            row = get_next_row();
         }
      

      Ich finde die beiden Varianten nehmen sich an Leserlichkeit nichts. Was für mich beide Ansätze unnötig schwierig zu verstehen macht, ist die gesamte Schleifenlogik. Das ist viel kognitiver Overhead ohne Nutzen und der Code ist auch schlecht wiederverwertbar, weshalb man automatisch in Code-Duplikations-Probleme läuft. Um das besser zu erklären, muss ich dein Beispiel etwas ergänzen. Dein Code-Kommentar suggeriert ja bereits, dass in der Schleife noch mehr Brechnungen stattfinden. In der Praxis sieht das oft so aus:

      const processed_rows = [];
      var row = get_next_row();
      while (row !== null) {
          processed_rows.push(process_row(row));
          row = get_next_row();
      }
      

      Das Problem hier ist, dass 3 Zeilen nur der Iteration dienen und nur eine Zeile wirklich Arbeit verrichtet (processed_rows.push(process_row(row))).

      Die Wurzel allen Übels ist die Funktion get_next_row[1]. Die Funktion versteckt den Zugriff auf eine Datenbank-Ergebnismenge. Da würde ich ansetzen: Die versteckte Abhängigkeit auflösen und explizit machen. Angenommen die Ergebnismenge liegt in einem Array result_set vor, dann ließe sich der Code schon deutlich reduzieren:

      const processed_rows = [];
      for (let row of result_set) {
          processed_rows.push(process_row(row));
      }
      

      Statt den drei Zeilen dient nun nur noch eine Zeile der Iteration und auch die können wir schnell eleminieren:

      const processed_rows = result_set.map(process_row)
      

      Es bleibt nur eine Zeile stehen, in der die gesamte Arbeit verrichtet wird. Der Code ist ist deutlich lesbarer und recyclebar: Werfen wir nun die Annahme über Board, dass result_set ein Array ist, und gehen stattdessen von einem Stream aus (in JavaScript sind asynchrone Datenstrukturen für Ein/Ausgabe-Funktionen die Regel und nicht die Ausnahme). Der Code müsste dafür nicht geändert werden und processed_rows wäre ohne weiteres Zutun sogar schon ein Stream. Zwei Fliegen mit einer Klappe.


      1. Das ist übrigens das Abstraction-Inversion Anti-Pattern. ↩︎

      1. Tach!

        Die Wurzel allen Übels ist die Funktion get_next_row[^1]. Die Funktion versteckt den Zugriff auf eine Datenbank-Ergebnismenge. Da würde ich ansetzen: Die versteckte Abhängigkeit auflösen und explizit machen. Angenommen die Ergebnismenge liegt in einem Array result_set vor, dann ließe sich der Code schon deutlich reduzieren:

        const processed_rows = [];
        for (let row of result_set) {
            processed_rows.push(process_row(row));
        }
        

        Statt den drei Zeilen dient nun nur noch eine Zeile der Iteration und auch die können wir schnell eleminieren:

        const processed_rows = result_set.map(process_row)
        

        Das setzt voraus, dass der Datenbankmechanismus das Resultset im Array zur Verfügung stellt oder in einer Art, dass es wie ein Array verarbeitbar ist. Traditionell war das unter PHP zum Beispiel nicht gegeben. Auch die funktionale Programmierung war noch nicht sehr ausgereift. Mittlerweile lässt es sich so schreiben:

        function process_row($row) {
            return $row; // in echt mit mehr Funktionalität
        }
        
        $processed_rows = array_map(
            function ($row) { 
              return process_row($row); 
            }, 
            $result->fetch_all(MYSQLI_ASSOC));
        

        Ist aber mehr Code als die nachfolgende herkömmliche Variante. $result ist nur Traversable, aber kein Array und array_map() möchte ein echtes Array haben. Das heißt, wenn es nicht wie im Falle PHPs bereits sowieso im Hintergrund passieren würde, dass erstmal das gesamte Resultset in den Speicher muss (fetch_all), statt zeilenweise verarbeitet werden zu könnnen. Moderne Systeme verhindern sowas, indem sie Generatoren verwenden, die sich nach außen hin wie Arrays geben, aber die Daten nur einzeln holen und herausgeben.

        function process_row($row) {
            return $row; // in echt mit mehr Funktionalität
        }
        
        $processed_rows = [];
        while ($row = $result->fetch_assoc()) {
            $processed_rows[] = process_row($row);
        }
        

        Die array_map-Zeile lässt sich vereinfachen, indem man die anonyme Funktion durch einen Callback ersetzt:

        function process_row($row) {
            return $row; // in echt mit mehr Funktionalität
        }
        
        $processed_rows = array_map('process_row', $result->fetch_all(MYSQLI_ASSOC));
        

        Nun hat man einen schönen Einzeiler wie bei dir am Ende. Aber wie ist es nun mit der Verständlichkeit? Unter PHP gibt es keine Möglichkeit einer Funktionsreferenz. Man muss deren Namen als String angeben. Das empfinde ich als großen Mist, wenn Strings eine code-ähnliche Bedeutung haben, also Magic Strings sind. Man weiß manchmal nicht, wenn zwei Strings zufällig oder unachtsamerweise denselben Wert haben, ob sie dann auch dieselbe Bedeutung haben. Das Dilemma entsteht nicht, wenn man richtige Code-Elemente verwendet. Die lassen sich eindeutig über den gesamten Code verfolgen, im Gegensatz zu Strings.

        Abgesehen davon versteckt das ebenso wie in deinem Javascript-Beispiel die API der Funktion process_row. Dass das map/array_map einen Parameter erzeugt, den es der Funktion übergibt, sieht man nicht, muss man wissen, ist also auch nicht besonders intuitiv.

        dedlfix.

        1. Lieber dedlfix,

          Unter PHP gibt es keine Möglichkeit einer Funktionsreferenz. Man muss deren Namen als String angeben.

          Ich mag mich täuschen, aber seit PHP5.3 ist das durchaus möglich! Dort kannst Du "anonyme" Funktionen definieren und in Variablen speichern:

          $f = function () { return 42; };
          
          $f(); // 42
          

          Das ist insbesondere nützlich, wenn Du Sortierfunktionen mit Callback nutzen möchtest:

          $last_name_first = function ($a, $b) {
            if ($a['last_name'] != $b['last_name']) {
              return strnatcasecmp($a['last_name'], $b['last_name']);
            }
          
            return strnatcasecmp($a['first_name'], $b['first_name']);
          };
          
          usort($member_list, $last_name_first);
          

          Wenn man die Sortierung nur einmal benötigt, darf man das auch gleich so notieren:

          usort($member_list, function ($a, $b) {
            if ($a['last_name'] != $b['last_name']) {
              return strnatcasecmp($a['last_name'], $b['last_name']);
            }
          
            return strnatcasecmp($a['first_name'], $b['first_name']);
          });
          

          Das empfinde ich als großen Mist, wenn Strings eine code-ähnliche Bedeutung haben, also Magic Strings sind. Man weiß manchmal nicht, wenn zwei Strings zufällig oder unachtsamerweise denselben Wert haben, ob sie dann auch dieselbe Bedeutung haben. Das Dilemma entsteht nicht, wenn man richtige Code-Elemente verwendet. Die lassen sich eindeutig über den gesamten Code verfolgen, im Gegensatz zu Strings.

          Wahrscheinlich hat man damals genau deswegen die anonymen Funktionen eingeführt. Die gibt es sogar mit Closures:

          $a = 2;
          $f = function ($x) use ($a) {
            return $x/$a;
          }
          
          $f(42); // 21
          

          Liebe Grüße,

          Felix Riesterer.

          1. Tach!

            Unter PHP gibt es keine Möglichkeit einer Funktionsreferenz. Man muss deren Namen als String angeben.

            Ich mag mich täuschen, aber seit PHP5.3 ist das durchaus möglich! Dort kannst Du "anonyme" Funktionen definieren und in Variablen speichern:

            Tatsächlich. Wenn man sie anonym hat, kann man sie über eine Variable referenzieren, als benannte Funktion jedoch nicht.

            Wenn man die Sortierung nur einmal benötigt, darf man das auch gleich so notieren:

            "So" meint in dem Fall direkt als anonyme Funktion am Ort der Verwendung, was ich ja auch als Beispiel in meinem Posting hatte.

            Das empfinde ich als großen Mist, wenn Strings eine code-ähnliche Bedeutung haben, also Magic Strings sind. Man weiß manchmal nicht, wenn zwei Strings zufällig oder unachtsamerweise denselben Wert haben, ob sie dann auch dieselbe Bedeutung haben. Das Dilemma entsteht nicht, wenn man richtige Code-Elemente verwendet. Die lassen sich eindeutig über den gesamten Code verfolgen, im Gegensatz zu Strings.

            Wahrscheinlich hat man damals genau deswegen die anonymen Funktionen eingeführt. Die gibt es sogar mit Closures:

            Vermutlich nicht deswegen, sondern weil man es als Feature von anderswo kannte und die Stimmen nach dem Habenwollen laut genug waren.

            Gut, das entschärft den Punkt mit den Magic Strings, aber die Verständlichkeit finde ich dadurch auch nicht großartig erhöht, wenn man da nun eine Variable hat, die da weiterhin den Funktionsaufruf versteckt.

            dedlfix.

            1. Gut, das entschärft den Punkt mit den Magic Strings, aber die Verständlichkeit finde ich dadurch auch nicht großartig erhöht, wenn man da nun eine Variable hat, die da weiterhin den Funktionsaufruf versteckt.

              Ich will gar nicht leugnen, dass meine Argumentation für PHP nicht so gut aufgeht wie für JavaScript. Funktionale Programmierung ist in JavaScript wesentlich prominenter als in PHP und im Umkehrschluss bietet PHP weniger Komfort für funktionale Programmierung - da beißt sich die Katze auch selber in den Schwanz. Zum Beispiel gibt es in PHP keinen asynchronen Event-Loop, also gibt es auch keinen Bedarf an asynchronen Datenstrukturen wie Streams oder Promises.

              Ich will trotzdem nochmal Wort für die funktionale Lösung erfreifen. Wir sind bis jetzt davon ausgegangen, dass wir nur ein Array durchlaufen. Dazu reicht eine einzige überschaubare Schleife. Das Iterieren wird allerdings umso schwieriger, je verzweigter die Datenstrukturen sind. Nehmen wir zum Beispiel einen Baum: Da reicht eine Schleife nicht mehr aus, und wie mans auch letztlich macht, man sieht dem Code auf einem flüchtigen Blick nicht mehr an, dass er der Iterations eines Baumes dient. Schlimmer wird es, wenn man den Baum mehrmals Iterieren möchte und immer wieder den gleichen Iterations-Algorithmus niederschreiben muss. Niemand will schließlich Code-Duplikation. Man lagert die Iterations-Logik also in eigenen Code aus. Da gibt es nun viele Möglichkeiten das zu tun, eine viel gesehene Variante ist das Iterator-Interface zu implementieren. Dann ließe sich der Baum wieder mit einer Schleife traversieren.

              foreach($tree as $node) {
                 $processed_nodes[] = process_node[$node];
              }
              

              Das sieht zunächst vielversprechend aus. Aber die Einfachheit ist trügerisch. Der Anwendungsfall den wir bisher betrachtet haben, hat die Iteration eines Arrays erfordert und uns am Ende ein Array gleicher Länge geliefert. Oder wir hatten einen Stream, den wir iteriert haben und hatten am Ende einen neuen Stream gleicher Länge. Nun wollen wir den Baum iterieren und am Ende einen Baum haben, der die selbe Struktur wie der vorherige aufweist: Also die gleiche Anzahl an Knoten, aber vor allem auch die gleichen Verzweigungen an korrespondierenden Knoten. Viel Glück mit dem Iterator-Interface, Stichworte RecursiveTreeIterator und RecursiveIteratorIterator.

              Das beschriebene Iterationsschema ist omnipräsent: Man hat einen Container-Typen, will mit jedem Wert darin eine Berechnung durchführen und am Ende den gleiche Container-Typen wieder haben. Das ist genau das, was die map-Funktion macht: Und zwar für Listen, Arrays, Streams, Promises (hier häufig then genannt), Bäume, Graphen etc… Und der Code ist immer sehr ähnlich und einfach:

              $processedArray = array_map($processItem, $array);
              $processedList = $list->map($processItem);
              $processedTree = $tree->map($processItem);
              // etc. pp
              

              Viel einfacher geht es eigentlich nicht mehr. Und da enden die Vorteile noch nicht: map ist nur eins von vielen wiederkehrenden Iterations-Schemen. Es gibt zich weitere: reduce, filter, zip, group… In den vollen Genuss kommt man natürlich erst, wenn man eine Library (z.B. pramda) einsetzt, die einem die diversen Algorithmen über konsistente Schnittstellen zugänglich macht.

              1. Lieber 1unitedpower,

                Zum Beispiel gibt es in PHP keinen asynchronen Event-Loop, also gibt es auch keinen Bedarf an asynchronen Datenstrukturen wie Streams oder Promises.

                dafür gibt es Sessions... grins

                Nehmen wir zum Beispiel einen Baum: Da reicht eine Schleife nicht mehr aus, und wie mans auch letztlich macht, man sieht dem Code auf einem flüchtigen Blick nicht mehr an, dass er der Iterations eines Baumes dient.

                Da greife ich zu rekursiven Strukturen, schreibe Funktionen, die sich selbst wieder aufrufen. Besser?

                Schlimmer wird es, wenn man den Baum mehrmals Iterieren möchte und immer wieder den gleichen Iterations-Algorithmus niederschreiben muss.

                Ist das bei rekursiven Funktionen auch notwendig?

                Niemand will schließlich Code-Duplikation.

                Eben.

                Nun wollen wir den Baum iterieren und am Ende einen Baum haben, der die selbe Struktur wie der vorherige aufweist: Also die gleiche Anzahl an Knoten, aber vor allem auch die gleichen Verzweigungen an korrespondierenden Knoten. Viel Glück mit dem Iterator-Interface, Stichworte RecursiveTreeIterator und RecursiveIteratorIterator.

                Aha! Recursive Tree Iterator! Da ist ja die Rekursion von der ich oben schrubte. :-)

                $processedArray = array_map($processItem, $array);
                $processedList = $list->map($processItem);
                $processedTree = $tree->map($processItem);
                // etc. pp
                

                Sieht echt wunderbar übersichtlich aus. Sollte ich mal demnächst probieren.

                In den vollen Genuss kommt man natürlich erst, wenn man eine Library (z.B. pramda) einsetzt, die einem die diversen Algorithmen über konsistente Schnittstellen zugänglich macht.

                Igitt!!! Not invented here!!! ;-P

                Liebe Grüße,

                Felix Riesterer.

                1. Spannende Diskussion, die ich da unabsichtlich losgetreten habe. FP ist hochinteressant, aber passt sie zur Frage?

                  Wenn ich fertige funktionale Operatoren wie .map() verwende, habe ich das Problem "Zuweisungen in einer bedingten Anweisung" locker ausgetrickst, denn dann programmiere ich die bedingte Anweisung gar nicht mehr selbst. Im Ergebnis wird die Frage des OP damit nicht beantwortet, sondern zu einen Problem Anderer Leute erklärt und dem OP der Bescheid erteilt, dass er das nicht zu wissen brauche.

                  Von Interesse war hier aber die untere Schicht. In PHP beispielsweise habe ich weder für PDO noch für das Filesystem einen fertigen Generator gefunden, der mir ein Traversable für eine SQL Ergebnismenge oder für die Zeilen einer Textdatei liefert. Auch in C#, was ich besser kenne als PHP, gibt's zwar LINQ für SQL, aber ein IEnumerable<string> für die Zeilen eines StreamReader muss ich mir immer noch selbst schreiben. Das ist dank yield keine besondere Mühe, aber tun muss ich es.

                  Man baut auf diese Weise eine Library aus Generatoren auf, aber irgendwann kommt doch wieder der Punkt, wo man auf low-level Ebene herumturnt (oder die Turnübungen anderer verstehen muss); und genau dort stellen sich die Fragen, die der OP hatte.

                  Rolf

                  1. Tach!

                    In PHP beispielsweise habe ich weder für PDO noch für das Filesystem einen fertigen Generator gefunden, der mir ein Traversable für eine SQL Ergebnismenge oder für die Zeilen einer Textdatei liefert.

                    PDO::query() liefert ein iterierbares Ergebnis und PDOStatement::fetchAll() ebenso. Filesystem-Iteratoren verstecken sich in der SPL. Außerdem gibt es glob(), das dir ein Array liefert.

                    dedlfix.

                    1. Filesystem-Iteratoren (-> Dateinamen) meinte ich nicht, sondern File-Iteratoren (-> Dateiinhalte).

                      fetchAll() liefert ein Array - richtig - was bei großen Ergebnismengen eventuell nicht das ist, was ich will. Ein Iterator sollte nicht zwangsweise die zu iterierende Menge als Array zwischenspeichern.

                      Bleibt die Traversable-Implementierung von PDOStatement - das ist eine Ecke von PHP die ich nicht im Detail kenne. Traversable definiert keine Methoden, sondern ist nur ein "Flag", dass das Objekt mit foreach iterierbar ist. Man soll für den täglichen Gebrauch ein Iterator oder IteratorAggregate Interface implementieren. Beides tut PDOStatement aber nicht. Enthält der PHP Kern Spezialcode zum Iterieren eines PDOStatement?

                      Rolf

                      1. Tach!

                        fetchAll() liefert ein Array - richtig - was bei großen Ergebnismengen eventuell nicht das ist, was ich will. Ein Iterator sollte nicht zwangsweise die zu iterierende Menge als Array zwischenspeichern.

                        Dann musst du sowieso anders an die Sache rangehen und explizit ein ungepuffertes Ergebnis verlangen, ansonsten ist die Ergebnismenge bereits beim Query im Hintergrund abgefragt und zwischengespeichert worden. Man muss dann aber auch die Frage stellen, warum du solch eine große Datenmenge mit PHP abfragen möchtest und nicht mit Paging zum Client hin eine Ergebnismenge in handelsüblicher Größe erzeugst.

                        Enthält der PHP Kern Spezialcode zum Iterieren eines PDOStatement?

                        Die Klärung der Frage wäre für mich nicht von Belang, weil es keine Auswirkungen auf die Verwendbarkeit hat. Vielleicht ist es ein Fehler im Handbuch, vielleicht ist die Klasse gar kein echtes PHP sondern im Handbuch nur so dargestellt (vermutlich das, wegen PECL-Vergangenheit), wer weiß. Egal.

                        dedlfix.

  4. Gibt es irgendeine Situation, wo ein solches Konstrukt sinnvoll angewendet wird (bzw. einen sinnvollen Nutzen ergibt) oder ist das einfach "logisch aber nutzlos"?

    Ja. Ich verwende das oft in meinen Parameter-Kontrollstrukturen wenn ein Parameter nicht nur eine bestimmte Aktion auslöst sondern gleichzeitig auch einen Wert liefert:

    # anstatt zuerst nur prüfen ob der
    # Parameter im Request ist
    if( $self->param('year') ) {
       $year = $self->param('year');
    }
    
    # hab ich hier Prüfung und Wertzuweisung
    # in einem Rutsch
    if( $year = $self->param('year') ) {}
    
    
    

    Schönen Sonntag.