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.
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. –
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. –
@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