什么时候在Javascript中设置调用堆栈?
在试图解决以下问题时:什么时候在Javascript中设置调用堆栈?,javascript,arrays,recursion,backtracking,callstack,Javascript,Arrays,Recursion,Backtracking,Callstack,在试图解决以下问题时: Generate all combinations of an array of string. Ex: Input: ['A', 'T', 'C', 'K'] Output: [ 'ATCK', 'ATC', 'ATK', 'AT', 'ACK', 'AC', 'AK', 'A', 'TCK', 'TC', 'TK', 'T', 'CK', 'C', 'K', '' ] 我有以下代码: function getSp
Generate all combinations of an array of string.
Ex: Input: ['A', 'T', 'C', 'K']
Output: [
'ATCK', 'ATC', 'ATK',
'AT', 'ACK', 'AC',
'AK', 'A', 'TCK',
'TC', 'TK', 'T',
'CK', 'C', 'K',
''
]
我有以下代码:
function getSpellableWords(arr) {
const res = [];
// helper(arr, res, 0, '');
helper(arr, res, 0, []);
return res;
}
function helper(arr, res, i, slate) {
const char = arr[i];
console.log(i, 'i')
if(i === arr.length) {
res.push(slate.join(''));
return;
}
// slate = slate + char;
slate.push(char)
console.log(slate, 'slate')
helper(arr, res, i+1, slate);
slate.pop()
helper(arr, res, i+1, slate);
}
getSpellableWords(['A', 'T', 'C', 'K']);
我的问题是:
如果删除代码中的以下行:
helper(arr, res, i+1, slate);
一旦i
等于5(即array.length),代码将在将slate
推入res
后停止。但是,如果我将该行保留,将设置一个调用堆栈,因此I
将从1->5上升,然后逐渐弹出到4
,然后是3
,然后返回到4
,依此类推。为什么会这样
澄清:因此我理解,对于每个递归调用,都会生成另一个
I
变量。然而,我希望第二个递归调用也能从1->4再次生成I
,但这次不是线性递增,而是进行回溯。为什么第一次调用中没有回溯,为什么第二次调用只生成第一个结果,而第二次调用生成其余的结果?每个helper
的递归调用确实会在调用堆栈上添加一个级别,这样当递归调用返回到调用方时,调用代码可以继续使用自己的本地执行上下文
helper
的每次执行都有自己的执行上下文,其中包括一个仅对该执行上下文可见的局部变量i
。它仅在调用堆栈中的该级别起作用
请注意,helper
代码从不更改其i
变量的值。当它被调用时,用作为第三个参数传递的任何值初始化它,这是它将拥有的唯一值
您注意到的对i
的更改实际上没有任何更改。对于i
所看到的每一个不同的值实际上都与恰好具有相同名称的不同变量有关
下面是一个关于i
变量寿命的小模式,用于res
变量的长度为2时(只是为了不让它太长!):
因此,我们看到,在这个特定的算法中,调用堆栈的大小(即递归树的深度)正好对应于当前执行上下文中变量
i
的值。当函数返回时,调用堆栈的大小减小(即递归深度减小),因此我们到达一个状态(从堆栈弹出)其中还有另一个i
实例,该实例的值也与调用堆栈的当前大小相匹配。每个helper
的递归调用确实会在调用堆栈上添加一个级别,以便当递归调用返回其调用方时,调用代码可以继续使用其自己的本地执行上下文
helper
的每次执行都有自己的执行上下文,其中包括一个仅对该执行上下文可见的局部变量i
。它仅在调用堆栈中的该级别起作用
请注意,helper
代码从不更改其i
变量的值。当它被调用时,用作为第三个参数传递的任何值初始化它,这是它将拥有的唯一值
您注意到的对i
的更改实际上没有任何更改。对于i
所看到的每一个不同的值实际上都与恰好具有相同名称的不同变量有关
下面是一个关于i
变量寿命的小模式,用于res
变量的长度为2时(只是为了不让它太长!):
因此,我们看到,在这个特定的算法中,调用堆栈的大小(即递归树的深度)正好对应于当前执行上下文中变量
i
的值。当函数返回时,调用堆栈的大小减小(即递归深度减小),因此我们到达一个状态(从堆栈弹出)其中还有另一个i
实例,该实例的值也与当前调用堆栈的大小相匹配。Trincot对该函数如何在内部工作给出了有用的详细响应。我只想指出一个重要的简化,你可以写:
const getSpellebleWords=([x,…xs])=>
x==未定义
? ['']
:((ps=getSpellableWords(xs))=>[…ps.map(p=>x+p),…ps])()
控制台日志(
GetSpelliablewords(['A','T','C','K']))
)
.as控制台包装{max height:100%!important;top:0}
Trincot对该函数如何在内部工作给出了有用的详细响应。我只想指出一个重要的简化,你可以写:
const getSpellebleWords=([x,…xs])=>
x==未定义
? ['']
:((ps=getSpellableWords(xs))=>[…ps.map(p=>x+p),…ps])()
控制台日志(
GetSpelliablewords(['A','T','C','K']))
)
作为控制台包装{max height:100%!重要;top:0}
,因为i
是一个局部变量,每个新执行的helper
都有自己的i
版本,可以有不同的值。当递归调用返回并且调用代码继续时,它自己的i
是相关的(再次)。因此我理解,对于每个递归调用,会生成另一个i
变量。然而,我希望第二个递归调用也能从1->4再次生成I
,但这次不是线性递增,而是进行回溯。为什么第一次调用中没有回溯,为什么第二次调用只生成第一个结果,而第二次调用生成其余结果?为什么您希望递归调用更改i
?从来没有。如果您查看代码,您会发现在helper
中没有对i
的赋值。唯一可能发生的事
helper(arr, res, 0, []); // The initial call
+--------top level helper execution context----+
| i = 0 |
| .... |
| slate.push(char) |
| helper(arr, res, i+1, slate); |
| +---nested helper execution context---+ |
| | i = 1 | |
| | .... | |
| | slate.push(char) | |
| | helper(arr, res, i+1, slate); | |
| | +--deepest exec. context-----+ | |
| | | i = 2 | | |
| | | ... | | |
| | | res.push(slate.join('')); | | |
| | | return; | | |
| | +----------------------------+ | |
| | // i is still 1 | |
| | slate.pop() | |
| | helper(arr, res, i+1, slate); | |
| | +----------------------------+ | |
| | | i = 2 | | |
| | | ... | | |
| | | res.push(slate.join('')); | | |
| | | return; | | |
| | +----------------------------+ | |
| | // i is still 1 | |
| +-------------------------------------+ |
| // i is still 0 |
| slate.pop() |
| helper(arr, res, i+1, slate); |
| +-------------------------------------+ |
| | i = 1 | |
| | .... | |
| | slate.push(char) | |
| | helper(arr, res, i+1, slate); | |
| | +----------------------------+ | |
| | | i = 2 | | |
| | | ... | | |
| | | res.push(slate.join('')); | | |
| | | return; | | |
| | +----------------------------+ | |
| | // i is still 1 | |
| | slate.pop() | |
| | helper(arr, res, i+1, slate); | |
| | +----------------------------+ | |
| | | i = 2 | | |
| | | ... | | |
| | | res.push(slate.join('')); | | |
| | | return; | | |
| | +----------------------------+ | |
| | // i is still 1 | |
| +-------------------------------------+ |
| // i is still 0 |
+----------------------------------------------+