Why does .foreach behave differently than for...of when executing a recursive callback?

Stuart :

I'm writing a recursive depth-first graph traversal using an adjacency list, which is an object containing vertices as keys and an array of each keys neighbors as values. A helper function is called recursively to visit all neighbors of an initial vertex, and then all neighbors of those neighbors, etc.

For some reason, using a "for...of" loop to iterate through each neighbor array is not working properly. The helper function will be called on a neighbor of a neighbor of a neighbor, but then when the base case is reached, the helper function does not seem to be called on the other neighbors of the initial neighbor, so you end up with dead-ends that are never reached.

  function depthFirstRecursiveTraversal(initialVertex) {
    const adjacencyList = {
      A: ['B', 'C'],
      B: ['A', 'D'],
      C: ['A', 'E'],
      D: ['B', 'E', 'F'],
      E: ['C', 'D', 'F'],
      F: ['D', 'E']
    };
    let visited = {};
    let vertexList = [];

    function dfsHelper(v) {
      if (!v) return null;
      vertexList.push(v);
      visited[v] = true;

      // Why does a for-of loop fail here? Recursion fails to reach all vertices
      //for (const neighbor of adjacencyList[v]) {
      //  if (!visited[neighbor]) return dfsHelper(neighbor);
      //}

      // But using forEach instead works!
       adjacencyList[v].forEach(neighbor => {
         if (!visited[neighbor]) return dfsHelper(neighbor);
       });

    }
    dfsHelper(initialVertex);

    return vertexList;
  }

Calling depthFirstRecursiveTraversal(A) returns [ 'A', 'B', 'D', 'E', 'C', 'F' ], which is correct.

But if you comment out the forEach and comment in the for...of loop, it returns [ 'A', 'B', 'D', 'E', 'C' ] -- the 'F' vertex is never reached.

For reference, this is what the graph looks like:

         A
       /   \
      B     C
      |     |
      D --- E
       \   /
         F

Can anyone tell me why for...of fails, but foreach works?

CertainPerformance :

Inside the for loop, return will terminate the entire dfsHelper function. In contrast, inside the forEach callback, return just terminates the callback.

The return value doesn't matter here, and returning doesn't achieve anything except introduce this bug. Best to remove the return from both versions - and if you do that, the for..of works fine:

function depthFirstRecursiveTraversal(initialVertex) {
  const adjacencyList = {
    A: ['B', 'C'],
    B: ['A', 'D'],
    C: ['A', 'E'],
    D: ['B', 'E', 'F'],
    E: ['C', 'D', 'F'],
    F: ['D', 'E']
  };
  let visited = {};
  let vertexList = [];

  function dfsHelper(v) {
    if (!v) return null;
    vertexList.push(v);
    visited[v] = true;

    for (const neighbor of adjacencyList[v]) {
      if (!visited[neighbor]) dfsHelper(neighbor);
    }

  }
  dfsHelper(initialVertex);

  return vertexList;
}
console.log(depthFirstRecursiveTraversal('A'));

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=301609&siteId=1