简体   繁体   English

递归 Function:返回 null

[英]Recursive Function: returning null

This particular puzzle from Eloquent JavaScript has been asked elsewhere on SO, but I haven't found one that relates to the specific question I have in reference to it.这个来自Eloquent JavaScript的特殊难题已在 SO 的其他地方提出,但我还没有找到与我提到的具体问题相关的问题。

function findSolution(target) {
  function find(start, history) {
    if (start == target)
      return history;
    else if (start > target)
      return null;
    else
      return find(start + 5, `(${history} + 5)`) ||
             find(start * 3, `(${history} * 3)`);
  }
  return find(1, "1");
}

console.log(findSolution(13));
// (((1 * 3) + 5) +5)

... and here's the call stack: ...这是调用堆栈:

find(1, "1")
  find(6, "(1 + 5)")
    find(11, "((1 + 5) + 5)")
      find(16, "(((1 + 5) + 5) + 5)")
        too big
      find(33, "(((1 + 5) + 5) * 3)")
        too big
    find(18, "((1 + 5) * 3)")
      too big
  find(3, "(1 * 3)")
    find(8, "((1 * 3) + 5)")
      find(13, "(((1 * 3) + 5) + 5)")
        found!

My question pertains to how return null;我的问题是关于如何return null; works here.在这里工作。 I'd like to better understand how this statement results in find() working back down the previously calculated target parameters;我想更好地理解这个语句如何导致find()回退先前计算的target参数; eg, find(33, .. --> find(18, .. --> find(3, .. . I understand how find(11 + 5, .. passes the call to find(11 * 3.. , but I don't understand how find(11 * 3.. passes the call to find(6 * 3, .. and then to find(1 * 3, .. , as the call stack above seems to indicate.例如, find(33, .. --> find(18, .. --> find(3, .. 。我了解find(11 + 5, ..如何将调用传递给find(11 * 3.. ,但我不明白find(11 * 3..如何将调用传递给find(6 * 3, ..然后再传find(1 * 3, .. ,正如上面的调用堆栈似乎表明的那样。

I understand how find(11 + 5, .. passes the call to find(11 * 3 .., but I don't understand how find(11 * 3 .. passes the call to find(6 * 3 , .. and then to find(1 * 3 , ..我了解find(11 + 5, .. 如何将调用传递给find(11 * 3 ..,但我不明白find(11 * 3 .. 如何将调用传递给find(6 * 3 , .. 和然后find(1 * 3 ,..

First note that there are two types of things that find may return: either a (non-empty) string ( history ) or null .首先请注意, find可能会返回两种类型的内容:(非空)字符串( history )或null The first is a truthy value, the second is a falsy value.第一个是真值,第二个是假值。

You understand that find(16, "(((1 + 5) + 5) + 5)") returns null and so the other part of the ||您了解find(16, "(((1 + 5) + 5) + 5)")返回null||的另一部分-expression is evaluated, ie find(33, "(((1 + 5) + 5) * 3)") . -expression 被评估,即find(33, "(((1 + 5) + 5) * 3)")

Just like the left part of ||就像||的左边部分can return null , so also the right part of it can return null .可以返回null ,所以它的右边部分也可以返回null In that case that whole expression returns null to the "parent" call, which also was made from such an ||在这种情况下,整个表达式将null返回到“父”调用,该调用也是由这样的|| expression.表达。

And so you really get the same thing happening there, but one level higher in the recursion tree.所以你真的会在那里发生同样的事情,但在递归树中更高一级。 The above was happening when at that higher level find(11, "((1 + 5) + 5)") was executing.上述情况发生在更高级别的find(11, "((1 + 5) + 5)")执行时。 It eventually returned null || null它最终返回null || null null || null , ie null , and so then (at that higher level) it goes to the other side of its own || null || null ,即null ,然后(在更高级别)它会走到它自己的另一边|| expression and evaluates find(18, "((1 + 5) * 3)") .表达式并计算find(18, "((1 + 5) * 3)") That also returns null (immediately this time).这也返回null (这次立即)。 And so we again have null || null所以我们再次有null || null null || null , and backtrack (that's the word.) to yet a higher level in the recursion tree. null || null ,然后回溯(就是这个词)到递归树中的更高级别。

The "Parent" “家长”

Recursion means that – while the function is executing – a new call of the same function is made.递归意味着——当 function 正在执行时——对相同的 function 进行新的调用。 I call the parent the function's execution that made the recursive call and which is waiting for that call to come back with a return value.我调用函数执行递归调用并等待该调用返回一个返回值。

That child may also become parent of yet a more nested, grandchild call.孩子也可能成为更嵌套的孙子电话的父母 So you have a stack of unfinished function executions.所以你有一堆未完成的 function 执行。 When the deepest of those returns a value, the one "above" it will "wake up" and receive that value: it can then continue and make another child call and a similar scenario repeats.当其中最深的返回一个值时,“上方”的那个将“唤醒”并接收该值:然后它可以继续并进行另一个子调用,并且类似的场景会重复。 If it has no more such calls to make, it will return a value to its parent (backtracking), which was all the time waiting for a recursive call to come back with a value.如果它不再需要进行此类调用,它将向其父级返回一个值(回溯),它一直在等待递归调用返回一个值。

Imagine this whole recursive process as a stack.将整个递归过程想象成一个堆栈。 When some code calls this function, the caller's execution context is pushed on a stack, and the execution continues with executing the function.当一些代码调用这个 function 时,调用者的执行上下文被压入堆栈,并继续执行 function。 This execution context holds everything the initiating code needs to proceed from the point where it makes the function call.此执行上下文包含启动代码从发出 function 调用的位置开始执行所需的所有内容。 It has variables with their values, the exact place in the code where it made the function call, the parameters it passed to that function call, ...etc.它有带有值的变量,在代码中进行 function 调用的确切位置,它传递给 function 调用的参数等等。

Then the function may call itself, so again its own execution context is pushed on the stack (the "call stack"), and so the stack may grow.然后 function 可能会调用自己,因此它自己的执行上下文再次被压入堆栈(“调用堆栈”),因此堆栈可能会增长。 But at some point the function will return a value, and the stack unwinds: the previous execution context is restored, and that parent will continue executing from where it made the recursive call.但是在某些时候 function 将返回一个值,并且堆栈展开:先前的执行上下文被恢复,并且该级将继续从它进行递归调用的位置执行。 Execution ends when this call stack is emptied.当此调用堆栈被清空时,执行结束。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM