区分对递归函数的初始调用和连续调用

Differentiate between an initial and successive call to a recursive function

本文关键字:调用 连续 递归函数      更新时间:2023-09-26

我的大问题是,在JavaScript中区分对递归函数的初始调用和连续调用的最简单方法是什么?

让我举个例子…

假设我希望下面的函数在初始调用中传递给函数的字符串为空时返回false。有没有一种方法可以做到这一点,而不添加另一个参数到函数?

function isPalindrome(str) {
   if (str.length <= 1) {
     return true;
   }
   if (str.charAt(0) !== str.charAt(str.length -1)) {
     return false;
   }
   return isPalindrome(str.substr(1, str.length - 2));
}
isPalindrome('') // returns true, but I want this to return false

顺便说一句,我知道上面的函数可以更简单地写成:

function isPalindrome(str) {
  return str == str.split('').reverse().join('');
}

但我将其重新定义为递归函数,以获得更广泛的问题…

不要试图区分不同的调用——函数的结果不应该依赖于副作用,更不应该依赖于调用堆栈。

相反,使用第二个函数来做一些不同的事情:
function isPalindrome(str) {
   return str.length <= 1 ||
     str.charAt(0) == str.charAt(str.length-1) && isPalindrome(str.slice(1, -1));
}
function isNonEmptyPalindrome(str) {
   return str.length > 0 && isPalindrome(str);
}

可以使用嵌套函数,甚至可以使用相同的名称:

function isPalindrome(str) {
  // The code out here is for the initial call:
  if (str === '')
    return false;
  // Then do the recursive call
  return function isPalindrome(str) {
    // within this scope, isPalindrome refers to this function
    if (str.length <= 1) {
      return true;
    }
    if (str.charAt(0) !== str.charAt(str.length -1)) {
      return false;
    }
    return isPalindrome(str.substr(1, str.length - 2));
  }(str); // call this function immediately
}

对于一般形式:

function name(arg) {
    // Setup code before initial call
    //////////////////////////////
    var retVal = function name(arg) {
      // recursive routine code
    }(arg);
    // Cleanup code after recursion completes
    /////////////////////////////////////////
    return retVal;
}

使用factorial

演示

这与Bergi的答案本质上是相同的,但是在isPalindrome内部声明了辅助函数,因此它不会在其他地方使用。

回文的一个更好的例子是,所有的标点符号都应该被删除,字母都应该大写或小写(这样比较就不区分大小写了),但只在第一次调用时。在那之后,就是比较字符的简单问题了。

length == zero部分也只处理一次,如果没有剩余字符可供比较,则不会递归调用该函数。

下面的代码进行初始处理,然后调用内部函数。

使用函数声明代替命名函数表达式,因为后者在IE中有不良的副作用。

  function isPalindrome(s) {
    // Initial processing only on first call
    // remove all punctuation
    s = s.replace(/[^a-z0-9]/ig,'').toLowerCase();
    return s.length == 0? false : doCheck(s);
    function doCheck(s) {
      if (s.length > 1) {
        if (s.substr(0,1) == s.substr(s.length - 1, 1)) {
          s = s.substr(1, s.length - 2);
          return s.length? doCheck(s) : true;
        } else {
          return false;
        }
      }
      return true;
    }
  }
  console.log(isPalindrome("Madam I'm Adam"));  // true
  console.log(isPalindrome("Madam I'm Addam")); // false