初学者 - 我的代码使用递归?

时间:2015-10-31 10:28:22

标签: javascript recursion

下面的函数检查数字是偶数还是奇数 - 简单。我的问题是关于调用堆栈。为了确保我理解它。

是否其他第二个if语句(数字< 0)调用主函数isEven(数字),所以它从顶部再次运行检查数字== 0等等?
是递归吗?

function isEven(number) {
  if (number == 0) 
    return true;
  else if (number == 1) 
    return false;
  else if (number<0) {
    number*=-1
    return isEven(number)
  }
  else 
    return isEven(number-2)
};


console.log(isEven(50));
// → true
console.log(isEven(75));
// → false
console.log(isEven(-1));
// → false

6 个答案:

答案 0 :(得分:2)

  

是第二个if语句(数字&lt; 0)调用main函数   isEven(数字)所以它从顶部再次运行检查数字== 0等等   上?

是的,没错。

  

是递归吗?

是的,它是递归工作原理的一个例子。

虽然它不是一个很好的例子,因为它可以很容易地重写为使用循环(甚至只是一个表达式),因此它没有显示递归的优点。

为了更好地展示递归是如何工作的,函数可以将数字分成两半并在每一半上调用自身:

function isEven(number) {
  if (number == 0) {
    return true;
  } else if (number == 1) {
    return false;
  } else if (number < 0) {
    return isEven(-number);
  } else {
    // split number in two halves
    var a = Math.floor(number / 2);
    var b = number - a;
    // number is even if the halves have the same status
    return isEven(a) == isEven(b);
  }
}

这会将工作分成两半并自称为每一半的工作。它将把工作变成越来越小的部分,直到每个部分都是确定01是否均匀的微不足道的工作。

通过将工件分成两半而不是削掉一小部分,零件很快变小。使用此实现调用isEven(10000)只会调用13级深度调用,而原始示例中的实现将调用5000级深度。

答案 1 :(得分:0)

是的,按预期工作。

是的,这是递归。和最后一个陈述中的那个一样。

请注意,很多人建议使用递归作为最后一个语句,所以你可以像这样重写它:

function isEven(number) {
  if(number < 0)
     number *= -1

  if (number == 0) 
    return true;
  else if (number == 1) 
    return false;
  else 
    return isEven(number-2)
};

答案 2 :(得分:0)

您可以看到它的递归,因为您的函数有停止条件(实际上有两个,数字== 0和数字== 1),然后自行调用。它是递归函数的一个非常基本的例子。

答案 3 :(得分:0)

  

是递归吗?

是的,确实如此。

答案 4 :(得分:0)

第二个条件是支持负数。我会把它放在自己的位置上:

function isEven(number) {
  return isPositiveEven( number < 0 ? 
                         number*-1 : 
                         number ); 
};

现在isPositiveEven看起来像这样,因为我们从来没有负数:

function isPositiveEven(number) {
  if (number == 0) 
    return true;
  else if (number == 1) 
    return false;
  else
    return isEven(number-2)
}

它是如何工作的,我们知道0和1的均匀度,你提供的数字越高,我们知道它必须是2或更高,并且可以用相同的减去2来递归,因为它不会改变奇数/偶数属性。例如。 isPositiveEven(3)将变为isPositiveEven(1)并将触及基本情况并返回false。对于isPositiveEven(2),它将变为isPositiveEven(0)并将触及基本情况并返回true

它是尾递归的,这意味着实际上不需要递归。根据循环编写的相同函数可能如下所示:

function isEven(number) {
  var n = number < 0 ? -1*number : number; // Math.abs(number)
  while( n > 1 ) {                         // n %= 2
    n -= 2;
  }
  return n == 0;
}

while循环是实际的模数运算,而JavaScript有运算符%,它也适用于负数-10 % 2 === 0,因此可以写成:

function isEven(number) {
  return number % 2 === 0;
}

答案 5 :(得分:0)

是的,你的例子是递归的。

任何包含可自动调用的函数都是递归的。

像这样的递归利用了JavaScript的递归函数调用功能。

在1960年的ALGOL-60之前(尽管Lisp是在那个时候定义的,它的编译器是后来的),语言不支持方便的用户域递归,原因有两个:

首先,早期语言基于注册。子程序指令将当前存储器位置保存在跳转地址中,然后将程序计数器设置为下一个地址。这比维护堆栈简单,但这意味着程序员必须在语言中有效地实现自己的递归。当引入基于堆栈的语言时,递归变得更容易。

其次,它被视为实现计算成本高昂(当时CPU非常杂乱)。

JavaScript是基于堆栈的语言的一个例子(因为大多数(全部?)现代语言都是)。

这意味着程序执行中的位置由堆栈数据结构定义。为此任务选择了堆栈,因为它提供了嵌套子例程调用所需的后进先出(LIFO)行为。

堆栈中的项目称为堆栈帧或简称​​ frames (有时称为激活记录),它们对应于调用的子例程在你的程序中(顶部框架是当前的)。每个帧包含与子例程调用相关联的元数据,并且每种语言都有所不同。

在JavaScript堆栈框架中称为执行上下文,EcmaScript规范定义了它们包含的元数据。

在你的例子中:

01    function isEven(number) {
02       if (number == 0) 
03         return true;
04       else if (number == 1) 
05         return false;
06       else if (number<0) {
07         number*=-1
08         return isEven(number)
09       }
10       else 
11         return isEven(number-2)
12    };
13
14    console.log(isEven(2)); // 2 chosen for simplicity.

// >> << indicates the currently executing expression.

          Expression           |      Current stack
--------------------------------------------------------
14 console.log(>>isEven(2)<<); |   [
                               |     { fn: global, arguments: [], ... }
                               |   ]
11 return >>isEven(0)<<;       |   [
                               |     { fn: isEven, arguments: [2], ... },
                               |     { fn: global, arguments: [], ... }
                               |   ]
03 >>return true<<;            |   [
                               |     { fn: isEven, arguments: [0], ... },
                               |     { fn: isEven, arguments: [2], ... },
                               |     { fn: global, arguments: [], ... }
                               |   ]
11 >>return isEven(0)<<;       |   [
                               |     { fn: isEven, arguments: [2], ... },
                               |     { fn: global, arguments: [], ... }
                               |   ]
14 >>console.log(isEven(2));<< |   [
                               |     { fn: global, arguments: [], ... }
                               |   ]
   >>end of program<<          |   [
                               |   ]