下面的函数检查数字是偶数还是奇数 - 简单。我的问题是关于调用堆栈。为了确保我理解它。
是否其他第二个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
答案 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);
}
}
这会将工作分成两半并自称为每一半的工作。它将把工作变成越来越小的部分,直到每个部分都是确定0
或1
是否均匀的微不足道的工作。
通过将工件分成两半而不是削掉一小部分,零件很快变小。使用此实现调用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<< | [
| ]