Javascript 这个递归函数如何改变';历史';变量

Javascript 这个递归函数如何改变';历史';变量,javascript,recursion,Javascript,Recursion,我觉得很快就能搞清这个问题,但我不太理解这个函数 我了解函数如何不断检查目标数,如果目标数太高,它将返回null,并应用第二个“| |”运算符 我不明白的是,一旦当前变量高于目标变量,它将开始返回历史变量,但没有额外的(+5)。这是如何从字符串中取出的 如有任何解释,将不胜感激。希望这是有道理的。我从Marijn Haverbeke的《雄辩的Javascript》一书中得到了这一点 函数查找解决方案(目标){ 函数查找(当前、历史){ 如果(当前==目标){ 回归历史; }否则如果(当前>目

我觉得很快就能搞清这个问题,但我不太理解这个函数

我了解函数如何不断检查目标数,如果目标数太高,它将返回null,并应用第二个“| |”运算符

我不明白的是,一旦当前变量高于目标变量,它将开始返回历史变量,但没有额外的(+5)。这是如何从字符串中取出的

如有任何解释,将不胜感激。希望这是有道理的。我从Marijn Haverbeke的《雄辩的Javascript》一书中得到了这一点


函数查找解决方案(目标){
函数查找(当前、历史){
如果(当前==目标){
回归历史;
}否则如果(当前>目标){
//log(`CURRENT AT NULL:`+CURRENT);
log(`HISTORY AT NULL:${HISTORY}`);
返回null;
}否则{
log(`${history}`);
返回find(当前+5,`(${history}+5)`)||
查找(当前*3,`(${history}*3)`);
}
}
返回结果(1,“1”);
}

控制台日志(findSolution(24))每当您的
current>目标
返回
null
并且您在
find(current*3,
(${history}*3)
时,将对其进行评估

假设

((((1 + 5) + 5) + 5) + 5)   ---> This is the current value of history
因此,当前值为
21

现在当你到达

   find(current + 5, `(${history} + 5)`) ||
        find(current * 3, `(${history} * 3)`);
它调用查找(21+5,
(${history}+5)
,因为
current>目标的值
,所以从该调用返回的值将为null,因为null是假值,所以将调用第二个操作数

  find(21 * 3, `(${history} * 3)`);   <--- so this is the final value returned form this invocation

函数查找解决方案(目标){
函数查找(当前、历史){
如果(当前==目标){
回归历史;
}否则如果(当前>目标){
log(当前'-->','HISTORY AT NULL:${HISTORY}`);
返回null;
}否则{
log(当前'-->','${history}`);
返回find(当前+5,`(${history}+5)`)||
查找(当前*3,`(${history}*3)`);
}
}
返回结果(1,“1”);
}

console.log(findSolution(8))每当您的
current>目标
返回
null
并且您在
find(current*3,
(${history}*3)
时,将对其进行评估

假设

((((1 + 5) + 5) + 5) + 5)   ---> This is the current value of history
因此,当前值为
21

现在当你到达

   find(current + 5, `(${history} + 5)`) ||
        find(current * 3, `(${history} * 3)`);
它调用查找(21+5,
(${history}+5)
,因为
current>目标的值
,所以从该调用返回的值将为null,因为null是假值,所以将调用第二个操作数

  find(21 * 3, `(${history} * 3)`);   <--- so this is the final value returned form this invocation

函数查找解决方案(目标){
函数查找(当前、历史){
如果(当前==目标){
回归历史;
}否则如果(当前>目标){
log(当前'-->','HISTORY AT NULL:${HISTORY}`);
返回null;
}否则{
log(当前'-->','${history}`);
返回find(当前+5,`(${history}+5)`)||
查找(当前*3,`(${history}*3)`);
}
}
返回结果(1,“1”);
}

console.log(findSolution(8))您正在通过递归进行所谓的深度优先搜索。每个人对递归的探索都有点不同。有些东西最终会让它为你点击

我想在你的代码中做一些注释,帮助它打印出更自然的文本。希望这能帮助一些人

但我认为最重要的是,通过一些缩进来想象递归的深度。当您在递归深度优先搜索期间进行日志记录时,您基本上是在生成一个二叉树,其输出如下所示:

根目录
根>左
根>右
它开始用递归嵌套,如下所示:

根目录
根>左
根>左>左
根>左>右
根>右
根>右>左
根>右>右
而不是“左”和“右”路径,您正在创建“+5”和“*3”探索分支。在递归中,首先探索树的+5分支以寻找解决方案。如果你没有找到它,那么你就去探索树的*3分支。当什么都没有找到时,这只意味着答案不在你考虑的树枝上,必须在树的前面做出不同的选择

当两种途径都尝试过,但都没有成功时,似乎会出现一些回溯。但实际上,这只是一个假设的结论,我们说“如果我们+5,我们能到达那里吗?”或者“如果我们*3,我们能到达那里吗?”如果答案都是否定的,那么你就不能从你现在的位置到达那里。你不必实际回溯,递归的美妙之处在于,你在搜索中找到了自己的位置,因为有人在“假设…”中调用了你。如果您的整个搜索结果为空,则没有问题。您只需放弃当前的搜索分支,调用您的“某人”将在另一个分支上尝试其他内容

递归的状态(您正在探索的分支)保存在调用堆栈上。这就是我们真正“备份”的地方

历史记录
永远不会被修改。我们只是探索通过递归构造的
历史
的不同版本。每个
history
都是它自己的副本,它只会变长(搜索树中的左分支或右分支)或被放弃,搜索会从递归中的其他
history
继续

下面是您的代码,其中包含一些缩进和对每一步的详细描述,希望能够更紧密地与递归联系起来

函数空间(缩进){
返回“”。重复(缩进);
}
函数查找解决方案(目标){
函数nope(缩进、历史){
console.log(