2017-02-09 1 views
4

Sagen, ich habe einen Baum, so etwas wie die folgenden, in javascript:Wie implementiert man ein rekursives Traversal innerhalb eines Iterator-Generators in Javascript?

let rootNode = {name:'', children:[ 
        {name:'0', children:[ 
         {name:'00', children:[]}, 
         {name:'01', children:[ 
          {name:'010', children:[]}, 
         ]}, 
         {name:'02', children:[]}, 
        ]}, 
        {name:'1', children:[ 
         {name:'10', children:[]}, 
        ]}, 
       ]}; 

Und ich möchte auf eine Vorordnungsdurchquerung tun, ich es so tun könnte:

function preOrderTraversalRecursive(rootNode, visitNodeCallback) { 
    function recurse(node) { 
     visitNodeCallback(node); 
     for (let childNode of node.children) 
      recurse(childNode); 
    } 
    recurse(rootNode); 
}; 
console.log("Pre-order traveral, recursive:"); 
preOrderTraversalRecursive(rootNode, function visitNodeCallback(node) { 
    console.log(" '"+node.name+"'"); 
}); 

die gibt die erwartete Ausgabe:

Pre-order traveral, recursive: 
    '' 
    '0' 
    '00' 
    '01' 
    '010' 
    '02' 
    '1' 
    '10' 

Jetzt sagen wir, ich möchte das gleiche tun ES6 Generator-Stil. Ich dachte, dass würde wie folgt aussehen:

function *preOrderGeneratorIteratorRecursive(rootNode) { 
    function recurse(node) { 
     yield node; 
     for (let childNode of node.children) 
      recurse(childNode); 
    } 
    recurse(rootNode); 
}; 
console.log("Pre-order generator iterator, recursive:"); 
for (let node of preOrderGeneratorIteratorRecursive(rootNode)) { 
    console.log(" '"+node.name+"'"); 
} 

Aber anscheinend das ist illegal: Uncaught SyntaxError: Unexpected strict mode reserved word am yield.

Enttäuschend! Gibt es eine Syntax, die ich vermisse? Was ist der sauberste Weg, um einen Generator diesen Algorithmus auszudrücken? Vielleicht mit einer Hilfsbibliothek?

Ich weiß, ich kann eine explizite Stack wie folgt verwenden, aber das ist aus mehreren Gründen nicht zufriedenstellend: (1) die Logik auf den Punkt verdeckt ist, wo es keine Lesbarkeit Vorteil um den Generator zu verwenden; könnte auch auf die rekursive-mit-Callback-Version zurückgehen, und (2) es ist nicht wirklich der gleiche Algorithmus, da es rückwärts über jeden node.children iteriert. Insbesondere bedeutet dies, dass es nicht funktioniert wenn node.children ist eine andere Art von iterable, vielleicht von einem anderen Generator.

function *preOrderTraversalIteratorGeneratorExplicitStackCheating(rootNode) { 
    let stack = [rootNode]; 
    while (stack.length > 0) { 
    let node = stack.pop(); 
    yield node; 
    for (let i = node.children.length-1; i >= 0; --i) // backwards 
     stack.push(node.children[i]); 
    } 
} // preOrderIteratorGeneratorExplicitStackCheating 
console.log("Pre-order traveral of tree with explicit stack, cheating:"); 
for (let node of preOrderTraversalIteratorGeneratorExplicitStackCheating(rootNode)) { 
    console.log(" '"+node.name+"'"); 
} 

Um klar zu sein: ich in einem Spezial-Helfer bin nicht daran interessiert, dass die schrecklichen Details eine expliziten-Stack-Traversal Implementierung versteckt. Ich möchte wissen, ob es eine Möglichkeit gibt, Iterator-Generator-Algorithmen klar zu schreiben, auch wenn sie rekursiv sind.

Antwort

2

Dies ist in der Tat mit dem Operator yield* vorgesehen. Im Wesentlichen müssen Sie dies als einen Generator betrachten, der seine Funktionalität an einen anderen delegiert. (. In der Tat können Sie zu jedem iterable Wert delegieren)

Zum Beispiel könnten Sie dies tun:

function *pointlessGenerator() { 
    yield* [1, 2, 3, 4]; 
} 

Diese 1 ergeben würde, 2, 3 und 4, weil yield* den Iterator läuft und liefert alle der Werte dieses Iterators der Reihe nach. Genau das gleiche Verhalten tritt auf, wenn Sie einen Generator übergeben.

In Ihrem Fall möchten Sie recurse einen Generator machen und rufen Sie dann mit yield*:

function *preOrderGeneratorIteratorRecursive(rootNode) { 
    function *recurse(node) { 
     yield node; 
     for (let childNode of node.children) 
      yield* recurse(childNode); 
    } 
    yield* recurse(rootNode); 
}; 
console.log("Pre-order generator iterator, recursive:"); 
for (let node of preOrderGeneratorIteratorRecursive(rootNode)) { 
    console.log(" '"+node.name+"'"); 
} 
0

Das Problem ist, dass Ihre recurse Funktion, in der Sie versuchen, yield zu verwenden, erklärt nicht als ein Generator. Deshalb erhalten Sie einen Syntaxfehler.Es wäre

function preOrderGeneratorIteratorRecursive(rootNode) { 
    function* recurse(node) { 
     yield node; 
     for (let childNode of node.children) 
      yield* recurse(childNode); 
    } 
    return recurse(rootNode); 
} 

oder einfach

function* preOrderGenerator(node) { 
    yield node; 
    for (let childNode of node.children) 
     yield* preOrderGenerator(childNode); 
} 
+0

Die ursprüngliche Art, wie ich es geschrieben sein müssen, 'recurse' wurde nicht als Generator bestimmt sind; es war nur ein Teil der Implementierung des Steuerflusses des umschließenden Generators. I.e. Die Ausbeute im Inneren sollte vom einschließenden Generator weichen; es sah für mich so aus, als wäre das die einzige mögliche sinnvolle Interpretation dessen, was ich geschrieben habe. Bei weiterem Nachdenken sehe ich jedoch, dass es für die Sprache problematisch wäre, dies zu erlauben: z.B. wenn eine Instanz des Bereichs der inneren Funktion länger als der Generator existiert. Ich denke, die Fälle, an denen ich interessiert bin, können mit yield * nahezu eindeutig ausgedrückt werden. –

+0

Ich denke, Sie fehlen ein '*' auf 'preOrderGeneratorIteratorRecursive' in Ihrem ersten Codeblock. Auch 'return recurse (rootNode);' muss geändert werden in 'yield * recurse (rootNode);', wie in @ lonesomeday's Antwort. –

+0

@DonHatch Ja, es wäre sehr problematisch und deshalb nicht erlaubt (nicht einmal eine Frage des "Lebens länger", sondern nur außerhalb des Generators genannt zu werden - der Kontrollfluss könnte schwer geschädigt werden. Nicht erlaubt sein, bekommt man Ein Syntaxfehler beim Versuch, yield in einer Nicht-Generator-Funktion zu verwenden – Bergi

Verwandte Themen