Javascript 如何保持和;“调试”;递归函数在我脑子里?
我有一个关于递归的问题:我应该如何“思考”来处理、保持和调试递归? 让我解释一下:例如,我们有一个计算斐波那契数的函数:Javascript 如何保持和;“调试”;递归函数在我脑子里?,javascript,algorithm,debugging,recursion,computer-science,Javascript,Algorithm,Debugging,Recursion,Computer Science,我有一个关于递归的问题:我应该如何“思考”来处理、保持和调试递归? 让我解释一下:例如,我们有一个计算斐波那契数的函数: function fib(n) { if(n < 3) return 1; return fib(n - 1) + fib(n - 2); } function fn(number) { if(number === 0) return 0; var fib = [1, 1]; for(var i = 2; i < num
function fib(n) {
if(n < 3) return 1;
return fib(n - 1) + fib(n - 2);
}
function fn(number) {
if(number === 0) return 0;
var fib = [1, 1];
for(var i = 2; i < number; i++) {
var temp = fib[i - 1] + fib[i - 2];
fib.push(temp);
}
return fib[fib.length - 1];
}
现在让我们看看斐波那契数的非递归函数:
function fib(n) {
if(n < 3) return 1;
return fib(n - 1) + fib(n - 2);
}
function fn(number) {
if(number === 0) return 0;
var fib = [1, 1];
for(var i = 2; i < number; i++) {
var temp = fib[i - 1] + fib[i - 2];
fib.push(temp);
}
return fib[fib.length - 1];
}
功能fn(编号){
如果(number==0)返回0;
var fib=[1,1];
对于(变量i=2;i
该函数的代码稍多一些,但它只包含一个循环,我可以轻松地将所有内容保留在脑海中,而不需要大量的递归级别
我作为示例展示的递归函数不是我的,我不明白应该如何实现这样的函数。
使用loop,一切都非常简单:
function fib(n, indent) {
console.log(indent + "fib(" + n + ")");
if(n < 3) return 1;
indent += " ";
return fib(n - 1, indent) + fib(n - 2, indent);
}
请注意,在简单的递归情况下,运行时是指数型的,而迭代版本是线性的(尝试20或30以查看差异——您实际上不需要数组,只需要最后两个值)可视化它
function fib(n, indent) {
console.log(indent + "fib(" + n + ")");
if(n < 3) return 1;
indent += " ";
return fib(n - 1, indent) + fib(n - 2, indent);
}
请注意,在简单的递归情况下,运行时是指数型的,而迭代版本是线性的(尝试20或30以查看差异——您实际上不需要数组,只需要最后两个值)以下是您应该如何看待递归函数
在此基础上,您可以根据您的用例形成更具体的策略。以下是您应该如何考虑递归函数
在此基础上,您可以根据您的用例形成更具体的策略。我觉得,简单地说,如果您可以使用
for
或while
循环做一些事情。如果不需要的话,不要刻意让它递归,因为它们可能会变得指数级(字面上)复杂,而且在调试过程中很痛苦
当你需要做一些需要递归的事情时,你就会知道了。你会知道,因为你会从一个
for
或循环开始,2天后意识到你不能做你需要的事情,你需要某种递归函数。我觉得,简单地说,如果你能用for
或而循环做一些你应该做的事情。如果不需要的话,不要刻意让它递归,因为它们可能会变得指数级(字面上)复杂,而且在调试过程中很痛苦
当你需要做一些需要递归的事情时,你就会知道了。你会知道的,因为你会从一个for
或开始,当循环时,2天后你会意识到你不能做你需要的事情,你需要某种递归函数。问题可能是你想做的太多了
递归函数是声明性的。在您提供的示例中,第n个Fibonacci是(n-1)Fibonacci数和(n-2)Fibonacci数的和。就这样。这就是你的函数所说的,这也是问题的确切解决方案:不需要思考
你通常只需要考虑基本情况
诀窍假设您的功能已经完美运行。不要考虑它是如何工作的。想象一下它已经在那里了,按照您需要的方式工作。你可以假装你甚至没有写这个函数——是别人写的,而且它可以工作
同样的方法适用于几乎任何递归解决的问题
假设您希望获得数组中的最小值。这与从数组中取出第一个值并询问“这个值比所有其他值小吗?”
i、 e。
在一个数组中,[1,2,3,4,5],第一个元素1是否比数组其余部分[2,3,4,5]中的最小元素小
我们知道我们可以得到[2,3,4,5]中的最小值,因为我们假设我们的函数有效
只剩下一件事,基本情况是什么
如果数组为空,那么最小值就没有意义,我们可能需要返回null类型值或引发异常
如果数组有1个元素,那么这必须是最小值,因为没有其他元素。伟大的因此,我们有:
function minimumValue(arr) {
if (arr.length == 0) {
// handle this problem
} else if (array.length == 1) {
let firstElement = arr[0];
return firstElement;
}
// assume the minimumValue function works
let firstElement = arr[0];
let restOfArray = arr.slice(1, arr.length);
return min(firstElement, minimumValue(restOfArray));
}
我什么都不用想。我只是把我想要的确切解决方案翻译成代码,它很有效,而且在我看来非常可读
如果你对数学感兴趣,那么你可以把它看作是归纳法的证明。假设它最多工作N次,您只需要编写N+1的情况。当然,不要忘记基本情况
边缘情况和其他问题是不可避免的,有时将递归调用扩展几次很有用,或者只是仔细检查编程的解决方案是否符合您心目中的实际解决方案。通常,这是一个仔细考虑基本情况的问题。
function minimumValue(arr) {
if (arr.length == 0) {
// handle this problem
} else if (array.length == 1) {
let firstElement = arr[0];
return firstElement;
}
// assume the minimumValue function works
let firstElement = arr[0];
let restOfArray = arr.slice(1, arr.length);
return min(firstElement, minimumValue(restOfArray));
}
f[0] = 0
f[1] = 1
f[n] = f[n-1] + f[n-2]
f[2] = f[1] + f[0] = 1 + 0 = 1
f[3] = f[2] + f[1] = (1 + 0) + 1 = 1 + 1 = 2
f[4] = f[3] + f[2] = 2 + 1 = 3
f[5] = f[4] + f[3] = 3 + 2 = 5
...
f[n] = f[n-1] + f[n-2]
var fib = function(n){
if( n === 0 || n === 1 )
return n;
return fib(n-1) + fib(n-2);
};
fib 0 = 0
fib 1 = 1
fib n = fib(n-1) + fib(n-2)
// get the sum of array
var sumArray = function( array ){
if( array.length === 1 ) // if only one element, it is the sum
return array[0];
return array.shift() + sumArray(array); // return array[0] + sumArray(array.slice(1))
};
console.log( sumArray( [1,2,3] ) );
// recursive functions don't have to return a value
var repeatFunction = function( action, times ){
if( times === 0 )
return;
action();
repeatFunction( action, times-1 );
};
repeatFunction( function(){ alert('jah'); }, 3 );
// used in function below
var repeatString = function( string, times ){
if( times < 1 )
return '';
return string + repeatString( string, times-1 );
};
console.log( repeatString( 'jah', 3 ) );
// and finally, a tree traversal
var readDOMStructure = function( element, level ){
if( typeof level === 'undefined' )
level = 0;
else
level++;
console.log( repeatString( ' ', level ) + element.nodeName );
for( var i=0, n=element.children.length; i<n; i++ )
readDOMStructure( element.children[i], level );
};
readDOMStructure( document.getElementById( 'jah' ) );