LanX!: generatoren mit yield

hi

ich versuche folgenden Code in FF3.0.19 zum laufen zu kriegen:

  
  
function gen() {  
   var i=0;  
   while (true)  yield i++ ;  
}  
var iter=gen();  
alert( iter());  
alert( iter());  
  

das erste alert sollte 0 ausgeben, das zwote 1 u.s.w.

FF beschwert sich aber dass vor dem yield ein ";" fehlen würde.

  
Fehler: missing ; before statement  
Quelldatei: file:///tmp/tst.html  
Zeile: 5, Spalte: 17  
Quelltext:  
   while (true)  yield i++  

Laut MDC ist das schon ab JS1.7 möglich und FF3.0 sollte schon 1.8 können.

https://developer.mozilla.org/en/new_in_javascript_1.7#Generators_and_iterators_(merge_into_Iterators_and_Generators)

Habe ich irgendwo Tomaten auf den Augen oder ist mein FF buggy?

Grüße
 rolf

  1. Hi,

    https://developer.mozilla.org/en/new_in_javascript_1.7#Generators_and_iterators_(merge_into_Iterators_and_Generators)

    Habe ich irgendwo Tomaten auf den Augen oder ist mein FF buggy?

    Antwort: Das mit dem Gemüse.

    „In order to use some of the new features of JavaScript 1.7, you need to specify that you wish to use JavaScript 1.7.“

    „The features that require the use of the new keywords "yield" and "let" require you to specify version 1.7 because existing code might use those keywords as variable or function names.“

    MfG ChrisB

    --
    RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
    1. Hi

      „In order to use some of the new features of JavaScript 1.7, you need to specify that you wish to use JavaScript 1.7.“

      „The features that require the use of the new keywords "yield" and "let" require you to specify version 1.7 because existing code might use those keywords as variable or function names.“

      Ich hab so was geahnt und wieder verworfen weil die List- respektive "Array Comprehensions" trotzdem funktionieren.

      Aber klar die brauchen keine neuen Keywords, for und if waren schon immer geschützt.

      Danke vielmals :)

      Rolf

  2. Der Vollständigkeit halber, so funktioniert es :

      
    <script type="application/javascript;version=1.7"/>  
      
    function gen() {  
       var i=0;  
       while (true)  
           yield i++;  
    }  
    var iter=gen();  
    alert( iter.next() );  
    alert( iter.next() );  
    </script>  
    
    
    1. Hallo LanX!,

      function gen() {
         var i=0;
         while (true)
             yield i++;
      }
      var iter=gen();

      könntest du mal kurz schreiben (für die, die jetzt die Doku noch nicht gelesen haben), was das mit dem "yield" auf sich hat? Für mich sieht das wie eine Endlosschleife aus.

      Gruß, Jürgen

      1. Hallo,

        function gen() {
           var i=0;
           while (true)
               yield i++;
        }
        var iter=gen();

        könntest du mal kurz schreiben (für die, die jetzt die Doku noch nicht gelesen haben), was das mit dem "yield" auf sich hat? Für mich sieht das wie eine Endlosschleife aus.

        Der Generator gen() erzeugt einen Iterator iter, auf dem man next() aufrufen kann. Das yield ist hier mehr wie ein return zu verstehen, es gibt beim Aufruf von next() das Ergebnis der Expression i++ zurück. Der Witz ist nun, dass i erhalten bleibt zwischen den verschiedenen Aufrufen von next, man es also hochzählen kann. Und die while-Schleife ist mehr eine Deklaration, die wird nicht wirklich so abgearbeitet. Sie gibt nur an, dass auf dem Iterator unendlich oft .next() aufgerufen werden kann (wenn ich das richtig verstehe).

        Mathias

        1. Hi,

          Das yield ist hier mehr wie ein return zu verstehen, es gibt beim Aufruf von next() das Ergebnis der Expression i++ zurück.

          Yield ist aus dem Englischen gar nicht so einfach zu übersetzen.
          Auf nordamerikanischen „Vorfahrt gewähren“-Schildern, den auf dem Kopf stehenden roten Dreiecken wie bei uns, steht z.B. teilweise auch “Yield” drauf - und in dem Sinne, mit „warte“, würde ich es auch hier übersetzen. Warte, bis die Verkehrslage dir erlaubt, weiter zu fahren - bzw. hier, bis dir per next() gesagt wird, „weitermachen“.

          Yield steigt also erst mal aus dem aktuellen Ablauf „aus“ - da hast du recht, es ist ein bisschen wie return. Nur halt mit dem Unterschied, dass man danach wieder „einsteigen“ kann, und dass die Rückgabe eines Wertes (in die Umgebung) erst durch Aufruf von next „ausgelöst“ wird.

          Und die while-Schleife ist mehr eine Deklaration, die wird nicht wirklich so abgearbeitet.

          Doch, abgearbeitet wird sie schon - nur nicht an einem Stück, sondern durch das yield unterbrochen.

          Sie gibt nur an, dass auf dem Iterator unendlich oft .next() aufgerufen werden kann (wenn ich das richtig verstehe).

          In dem gezeigten Beispiel schon. Du musst aber natürlich keine unendlich laufende Schleife nehmen, sondern kannst den Lauf auch begrenzen:

          function gen() {  
             var i=0;  
             while (i<5)  
                 yield i++;  
          }  
          var iter=gen();  
          for(var x=0; x<10; ++x) {  
            alert( iter.next() );  
          }
          

          Das liefert dir alert-Meldungen mit den Werten 0 bis 4 - und danach hagelt es eine
          uncaught exception: [object StopIteration]

          • weil die Schleife eben nicht mehr „fortgesetzt“ werden kann.

          Man muss wohl diese Exception abfangen - eine Möglichkeit, vorher abzufragen, ob es noch ein nächstes „Element“ gibt, scheint mir nicht vorgesehen zu sein.

          Welchen praktischen Nutzen die send-Methode haben soll, kann ich allerdings noch nicht ganz erkennen.

          “Once a generator has been started by calling its next() method, you can use send(), passing a specific value that will be treated as the result of the last yield. The generator will then return the operand of the subsequent yield.”

          function gen() {  
             var i=0;  
             while (i<10)  
                 yield i++;  
          }  
          var iter=gen();  
          for(var x=0; x<4; ++x) {  
            alert(iter.next());  
          }  
          alert("send: "+iter.send(1));  
          for(var x=0; x<4; ++x) {  
            alert(iter.next());  
          }
          

          Hier erhalte ich jedes Mal 0, 1, 2, 3, send: 4, 5, 6, 7, 8 - egal, ob ich als Argument für send 1, 47 oder 376 verwende.

          MfG ChrisB

          --
          RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
          1. Hallo ChrisB,

            Und die while-Schleife ist mehr eine Deklaration, die wird nicht wirklich so abgearbeitet.

            Doch, abgearbeitet wird sie schon - nur nicht an einem Stück, sondern durch das yield unterbrochen.

            damit müsste man doch so etwas (Timerfunktion) realisieren können, oder das, was ich hier (weiter bei Klick) zu erklären versuche.

            Gruß, Jürgen

            1. damit müsste man doch so etwas (Timerfunktion) realisieren können, oder das, was ich hier (weiter bei Klick) zu erklären versuche.

              Ja, Generators/Iterators sind eine gute Möglichkeit, um bei asynchronen Ereignissen mit der Abarbeitung einer Liste einfach dort weiterzumachen, wo man aufgehört hat - ohne dass die Status-Daten in einer Closure oder einem Objekt gespeichert werden.

              Es gibt übrigens verschiedene Kontrollfluss-Metasprachen wie z.B. TameJS die es vereinfachen, solche Abläufe zu definieren.

              Mathias

          2. function gen() {

            var i=0;
               while (i<10)
                   yield i++;
            }
            var iter=gen();
            for(var x=0; x<4; ++x) {
              alert(iter.next());
            }
            alert("send: "+iter.send(1));
            for(var x=0; x<4; ++x) {
              alert(iter.next());
            }

            
            >   
            > Hier erhalte ich jedes Mal 0, 1, 2, 3, send: 4, 5, 6, 7, 8 - egal, ob ich als Argument für send 1, 47 oder 376 verwende.  
              
            Du machst mit dem Wert, den du an den Iterator sendest, ja auch nichts. Der Iterator muss ihn entgegennehmen:  
              
            <script type="application/javascript;version=1.8">  
            ~~~javascript
            function gen() {  
            	var i=0;  
            	while (i<10) {  
            	var sentValue = yield i++;  
            	if (typeof sentValue == 'number')  
            		i = sentValue;  
            	}  
            }  
            var iter=gen();  
            for(var x=0; x<4; ++x) {  
            	console.debug(iter.next());  
            }  
            console.debug('send', iter.send(5));  
            for(var x=0; x<4; ++x) {  
            	console.debug(iter.next());  
            }
            

            </script>

            Mathias

            1. Hi,

              Du machst mit dem Wert, den du an den Iterator sendest, ja auch nichts. Der Iterator muss ihn entgegennehmen:

              Danke, das war aus der Beschreibung auf der Mozilla-Seite für mich nicht erkenntbar.

              function gen() {

              var i=0;
              while (i<10) {
              var sentValue = yield i++;
              if (typeof sentValue == 'number')

                
              yield hat also einen „Rückgabewert“, aber nur in dem Fall, wenn ich von außen etwas sende - andernfalls ist sentValue nur undefined.  
                
              Erscheint mir irgendwie umständlich und unschön. Frage mich, was dagegen sprach, beim send-en gleich den Wert der ge-yield-eten Variablen auf den Wert zu setzen?  
                
              MfG ChrisB  
                
              
              -- 
              RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
              
              1. yield hat also einen „Rückgabewert“, aber nur in dem Fall, wenn ich von außen etwas sende - andernfalls ist sentValue nur undefined.

                Erscheint mir irgendwie umständlich und unschön. Frage mich, was dagegen sprach, beim send-en gleich den Wert der ge-yield-eten Variablen auf den Wert zu setzen?

                Dass es keine einzelne Variable gibt, die dafür gedacht ist.

                send übergibt einfach einen Wert an den Iterator, welcher das Ergebnis des yield-Ausdrucks ist.

                Und yield [wert] übergibt ebenfalls nur einen Wert, welcher dann das Ergebnis des Ausdrucks iterator.next() ist. yield übergibt keine Variable. Man kann auch yield 'foo' schreiben.

                (Vermutlich sind in beiden Fällen mittels Destructuring Assignment auch mehrere Werte möglich.)

                Was der Iterator mit diesem Wert macht, ist natürlich seine Sache. Er kann den Wert einer Variablen zuzuweisen oder sonst irgendetwas damit machen.

                Im Grunde ist next und yield ein Ping-Pong zwischen zwei Anweisungsblöcken, dabei können sie sich Werte hin- und zurückgeben. Was die jeweils mit den übergebenen Werten tun, ist Sache des Programmierers.

                Mathias

          3. Hi

            Yield ist aus dem Englischen gar nicht so einfach zu übersetzen.

            Ich würd nicht zuviel auf die Wortbedeutung geben, da yield in Ruby wieder anders funtioniert.

            Es ist Wortverwandt mit dem deutschen "gelten" im Sinne von "zahlen".

            Ich würds es grob "als einen Ertrag erbringen" übersetzen.

            siehe http://dict.leo.org/?lp=ende&search=yield

      2. Hi

        könntest du mal kurz schreiben (für die, die jetzt die Doku noch nicht gelesen haben), was das mit dem "yield" auf sich hat? Für mich sieht das wie eine Endlosschleife aus.

        Warum hast du die Doku nicht gelesen? :)

        Einstein wurde mal von nem Reporter gefragt ob er die  Relativitätstheorie in 10 min erklären könne. Natürlich könne er das, aber ob der andere es verstünde sei ne andere Frage... ;-)

        OK, das Konzept ist fast 1 zu 1 von Python abgekupfert und in JS eigentlich nur theoretisch interessant, weil es nicht zum ECMA Standard gehört. wenn du nach Generator/Iterator/Python googelst wirst du zig deutschsprachige Quellen mit ausführlichen Beispielen dazu finden.

        Nun die Kurzerklärung: Indem du in einer Funktion das "return" durch ein (oder mehrere) "yield" ersetzt wandelt es sich magisch in eine(n) Generator(funktion), (was im Grunde ein funktionaler Konstruktor ist)

        Diese liefert bei jedem Aufruf ein Iterator-Objekt zurück, statt den Body auszuführen. Dieser Funktionskörper beschreibt nämlich den Code der next-methode des Objekts.

        Bei jedem Aufruf dieser next()-Methode wird genau an der Stelle weitergemacht wo sie das letztemal durch yield verlassen wurde (bei einem return würde wieder von vorne angefangen). Da alle lokalen Variablenzustände zwischen den Aufrufen erhalten bleiben statt reinitialisiert zu werden, wird die Methode nicht davon beeinflusst, ob sie jemals verlassen wurde (solche vars kennt man glaub ich auch als "static" in C und PHP)

        Das ganze ist also ein Spezialfall von Ko-Routinen, die wiederum als eine Sonderform des Multitasking verstanden werden kann.

        Um dir die Vorteile klar zu machen, könntest du versuchen ein Iterator-Objekt klassisch zu erzeugen. Dann müssten alle  Zustandsvariablen ins Closure von next() verschoben werden, um statisch zu bleiben, außerdem müsstest du dafür sorgen dass sie nur einmal initialisiert werden.

        Alle Klarheiten beseitigt? :)

        Ich finde ehrlich gesagt Guido's Design etwas fragwürdig, weil ein Anfänger intuitiv davon ausgehen muss, dass ein Aufruf eines Generators gen() auch dessen Code ausführt (schließlich steht "function" bzw "def" davor).

        Die Gründe werden auch in in einer [href:http://www.python.org/dev/peps/pep-0255/@title=PEP] erläutert, ich persönlich finde Larry's Vorschlag für gather/take in Perl6 klarer, weil "gather" eben kein "sub" ist.

        Product Placement:

        Wie man gather/take pragmatisch in Perl5  implementieren kann ist Inhalt eines Talks dass ich auf der nächsten YAPC::EU in Riga und auf dem Deutschen Perl Workshop im Oktober in Frankfurt halten werde.

        Cheers
          Rolf

        1. Hallo LanX!,

          Warum hast du die Doku nicht gelesen? :)

          ich dachte, wenn dieser Thread mal von Google im Archiv gefunden wird, bringt er dem Sucher auch etwas. Und mir natürlich auch.

          Vielen Dank, auch an Matthias.

          Gruß, Jürgen