Рекурсивные генераторы в JavaScript

Я пытаюсь написать рекурсивный генератор для прохождения заказа.

class Tree {
  *inOrderTraversal() {
    function* helper(node) {
      if (node.left !== null) {
        // this line is executed, but helper is not being called
        helper(node.left); 
      }
      yield node.value;
      if (node.right !== null) {
        helper(node.right);
      }
    }

    for (let i of helper(this.root)) {
      yield i;
    }
  }
  // other methods omitted
}

и я вызываю генератор так:

const tree = new Tree();
tree.add(2);
tree.add(1);
tree.add(3);

for (let i of tree.inOrderTraversal()) {
    console.log(i); // only prints 2
}

почему генератор только уступает 2? Почему это как минимум не уступая 1 до 2?

как я могу это исправить?

если это поможет, я транспилирую код с помощью babel.

babel --optional runtime test.js | node

2 ответов


проблема была не в рекурсии. Ваша функция сделал вызовите себя рекурсивно, он просто не дал значений снаружи. Когда вы вызываете helper (), вы получаете итератор в качестве возвращаемого значения, но вы хотели, чтобы итерационные значения этого итератора были получены. Если вы хотите уступить рекурсивно, вам нужно yield *. Попробуйте так:

  * inOrderTraversal() {
    function* helper(node) {
      if (node.left !== null) {
        // this line is executed, but helper is not being called
        yield * helper(node.left); 
      }
      yield node.value;
      if (node.right !== null) {
        yield * helper(node.right);
      }
    }

    for (let i of helper(this.root)) {
      yield i;
    }
  }

и пока вы на него, вы можете заменить for петли с:

yield * helper(this.root)

helper(node.left); вызывает функцию и создает генератор, но тело функции генератора никогда не выполняется, потому что генератор никогда не расширяется. Чтобы переслать все его значения генератору тока, вы можете использовать yield* ключевое слово, который работает так же, как

for (let i of helper(this.root))
    yield i;

вы использовали в своей inOrderTraversal метод. И действительно, это должно было быть yield* так же хорошо или даже лучше, нет никаких причин, чтобы сделать inOrderTraversal функция генератора, когда он может просто будьте обычным методом, который возвращает генератор:

class Tree {
  inOrderTraversal() {
    function* helper(node) {
      if (node.left !== null)
        yield* helper(node.left);
      yield node.value;
      if (node.right !== null)
        yield* helper(node.right);
    }

    return helper(this.root);
  }
  … // other methods
}