Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

recursion not working without declaring global variable

Why does version A work but version B doesn't? How can I make version B work without declaring a global variable outside the function (which is bad practice)? I'm not clear on why I can't just declare count inside the function itself.

A)

  var count = 0;

  var containsFiveOrMoreDivs = function(domElement) {

    if (domElement && domElement.tagName === "DIV") {
      count++;
    }


    //base case: 

    if (count >= 5) {
      return true;
    } else {
      if (domElement.hasChildNodes()) {
        var children = domElement.childNodes;
        for (var i = 0; i < children.length; i++) {

          if (containsFiveOrMoreDivs(children[i])) {
            return true;
          }

        }
      }
      return false;
    }
  };

B)

 var containsFiveOrMoreDivs = function(domElement) {
    var count = 0;
    if (domElement && domElement.tagName === "DIV") {
      count++;
    }


    //base case: 

    if (count >= 5) {
      return true;
    } else {
      if (domElement.hasChildNodes()) {
        var children = domElement.childNodes;
        for (var i = 0; i < children.length; i++) {

          if (containsFiveOrMoreDivs(children[i])) {
            return true;
          }

        }
      }
      return false;
    }
  };
like image 981
devdropper87 Avatar asked Aug 30 '15 00:08

devdropper87


1 Answers

What you really need is two functions, one inside the other:

function containsFiveOrMoreDivs(domElement) {
  var count = 0;
  function doCount(domElement) {
      if (domElement && domElement.tagName === "DIV") {
        count++;
      }

      //base case: 

      if (count >= 5) {
        return true;
      }
      else {
        if (domElement.hasChildNodes()) {
          var children = domElement.childNodes;
          for (var i = 0; i < children.length; i++) {

            if (doCount(children[i])) {
              return true;
            }

          }
        }
        return false;
      }
   }
   return doCount(domElement);
}

In that setup, you pass in an element reference, and then the outer function calls the inner function after initializing the counter.


original not very good answer here

Your second version ("B") has "count" as a local variable of the function. Each invocation of the function gets its very own "count" variable, and in each invocation the first thing that happens is that it's initialized to zero.

If you don't want a global, you can use a closure:

 var containsFiveOrMoreDivs = function() {
    var count = 0;
    return function(domElement) {
      if (domElement && domElement.tagName === "DIV") {
        count++;
      }

      //base case: 

      if (count >= 5) {
        return true;
      } else {
        if (domElement.hasChildNodes()) {
          var children = domElement.childNodes;
          for (var i = 0; i < children.length; i++) {

            if (containsFiveOrMoreDivs(children[i])) {
              return true;
            }

          }
        }
        return false;
      }
    };
  }();

That code wraps your actual counter function in an anonymous function that includes the "count" variable. It won't be global; it'll be completely private to the "containsFiveOrMoreDivs" function. This is like the best of both worlds: you get to treat "count" as a global, but it's not global. You don't need to worry about carrying a parameter around either.

like image 50
Pointy Avatar answered Oct 24 '22 00:10

Pointy