【问题标题】:How Is This Recursive Function Changing The 'history' Variable? [duplicate]这个递归函数如何改变“历史”变量? [复制]
【发布时间】:2020-02-04 09:01:57
【问题描述】:

我感觉很接近解决这个问题,但我不太明白这个功能..

我知道该函数如何不断检查目标数字,如果它变得太高,它返回 null 和第二个 '||'运算符被应用。

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

任何解释将不胜感激。希望这是有道理的。我是从 Marijn Haverbeke 的 Eloquent Javascript 一书中摘录的


function findSolution(target) {
  function find(current, history) {
    if (current == target) {
      return history;
    } else if (current > target) {
      //console.log(`CURRENT AT NULL: ` + current);
      console.log(`HISTORY AT NULL:  ${history}`);
      return null;

    } else {
      console.log(`${history}`);
      return find(current + 5, `(${history} + 5)`) ||
        find(current * 3, `(${history} * 3)`);
    }
  }
  return find(1, "1");
}
console.log(findSolution(24));
// → (((1 * 3) + 5) * 3)

【问题讨论】:

  • 你看过this question吗?
  • "一旦当前变量高于目标变量,它将开始返回历史变量" - 否。当电流 等于 与目标变量时,它会 return history

标签: javascript recursion


【解决方案1】:

您正在通过递归进行所谓的深度优先搜索。每个人对递归的理解都略有不同。有些东西最终会让你点击。

我想在您的代码中添加一些注释,以帮助它打印出更自然的所发生情况的记录。希望这会有所帮助。

但我认为最重要的是通过缩进可视化递归的深度。当您在递归深度优先搜索期间记录时,您基本上是在制作一棵二叉树,其输出如下:

Root
   Root > Left
   Root > Right

它开始像这样递归嵌套:

Root
   Root > Left
      Root > Left > Left
      Root > Left > Right
   Root > Right
      Root > Right > Left
      Root > Right > Right

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

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

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

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

所以这是您的代码,其中包含一些缩进和一些冗长的描述,希望能更紧密地与递归相关联。

function spaces(indent) {
  return '    '.repeat(indent);
}

function findSolution(target) {
  function nope(indent, history) {
    console.log(spaces(indent) + `Sadly we've exhausted all possible ways of getting there from this starting point.  We may still be able to get to ${target}, but not by starting from ${history}.`);
    return false;
  }
  function badnews(history) {
    console.log(`I've tried everything and there's just no way of getting to ${target}.  :(`);
    return false;
  }
  function find(current, which, history, indent) {
    if (current == target) {
      console.log(spaces(indent) + `${which}, and guess what...we finally found a solution! Because ${history} = ${current}.  So we can stop now. :)`);
      return history;
    } else if (current > target) {
      //console.log(`CURRENT AT NULL: ` + current);
      console.log(spaces(indent) + `${which}, we reached a dead end because ${history} = ${current} which is unfortunately already bigger than ${target}.`);
      return null;

    } else {
      console.log(spaces(indent) + `${which}, ${history} looks promising because it equals ${current}, which is still less than ${target}.  We'll try two ways of getting to ${target} from here.`);
      return find(current + 5, 'First, by adding 5', `(${history} + 5)`, indent+1) ||
        find(current * 3, 'Second, by multiplying by 3', `(${history} * 3)`, indent+1) ||
        nope(indent+1, history);
    }
  }
  return find(1, 'Initially', "1", 0) || badnews();
}
console.log(`${findSolution(24)}`);

输出复制如下,以防万一。抱歉,没有包装输出,因为缩进更重要,因此您可以查看递归的深度,以及导致回溯的原因。如果您发现 sn-p 控制台输出更具可读性,则可以运行 sn-p。

Initially, 1 looks promising because it equals 1, which is still less than 24.  We'll try two ways of getting to 24 from here.
    First, by adding 5, (1 + 5) looks promising because it equals 6, which is still less than 24.  We'll try two ways of getting to 24 from here.
        First, by adding 5, ((1 + 5) + 5) looks promising because it equals 11, which is still less than 24.  We'll try two ways of getting to 24 from here.
            First, by adding 5, (((1 + 5) + 5) + 5) looks promising because it equals 16, which is still less than 24.  We'll try two ways of getting to 24 from here.
                First, by adding 5, ((((1 + 5) + 5) + 5) + 5) looks promising because it equals 21, which is still less than 24.  We'll try two ways of getting to 24 from here.
                    First, by adding 5, we reached a dead end because (((((1 + 5) + 5) + 5) + 5) + 5) = 26 which is unfortunately already bigger than 24.
                    Second, by multiplying by 3, we reached a dead end because (((((1 + 5) + 5) + 5) + 5) * 3) = 63 which is unfortunately already bigger than 24.
                    Sadly we've exhausted all possible ways of getting there from this starting point.  We may still be able to get to 24, but not by starting from ((((1 + 5) + 5) + 5) + 5).
                Second, by multiplying by 3, we reached a dead end because ((((1 + 5) + 5) + 5) * 3) = 48 which is unfortunately already bigger than 24.
                Sadly we've exhausted all possible ways of getting there from this starting point.  We may still be able to get to 24, but not by starting from (((1 + 5) + 5) + 5).
            Second, by multiplying by 3, we reached a dead end because (((1 + 5) + 5) * 3) = 33 which is unfortunately already bigger than 24.
            Sadly we've exhausted all possible ways of getting there from this starting point.  We may still be able to get to 24, but not by starting from ((1 + 5) + 5).
        Second, by multiplying by 3, ((1 + 5) * 3) looks promising because it equals 18, which is still less than 24.  We'll try two ways of getting to 24 from here.
            First, by adding 5, (((1 + 5) * 3) + 5) looks promising because it equals 23, which is still less than 24.  We'll try two ways of getting to 24 from here.
                First, by adding 5, we reached a dead end because ((((1 + 5) * 3) + 5) + 5) = 28 which is unfortunately already bigger than 24.
                Second, by multiplying by 3, we reached a dead end because ((((1 + 5) * 3) + 5) * 3) = 69 which is unfortunately already bigger than 24.
                Sadly we've exhausted all possible ways of getting there from this starting point.  We may still be able to get to 24, but not by starting from (((1 + 5) * 3) + 5).
            Second, by multiplying by 3, we reached a dead end because (((1 + 5) * 3) * 3) = 54 which is unfortunately already bigger than 24.
            Sadly we've exhausted all possible ways of getting there from this starting point.  We may still be able to get to 24, but not by starting from ((1 + 5) * 3).
        Sadly we've exhausted all possible ways of getting there from this starting point.  We may still be able to get to 24, but not by starting from (1 + 5).
    Second, by multiplying by 3, (1 * 3) looks promising because it equals 3, which is still less than 24.  We'll try two ways of getting to 24 from here.
        First, by adding 5, ((1 * 3) + 5) looks promising because it equals 8, which is still less than 24.  We'll try two ways of getting to 24 from here.
            First, by adding 5, (((1 * 3) + 5) + 5) looks promising because it equals 13, which is still less than 24.  We'll try two ways of getting to 24 from here.
                First, by adding 5, ((((1 * 3) + 5) + 5) + 5) looks promising because it equals 18, which is still less than 24.  We'll try two ways of getting to 24 from here.
                    First, by adding 5, (((((1 * 3) + 5) + 5) + 5) + 5) looks promising because it equals 23, which is still less than 24.  We'll try two ways of getting to 24 from here.
                        First, by adding 5, we reached a dead end because ((((((1 * 3) + 5) + 5) + 5) + 5) + 5) = 28 which is unfortunately already bigger than 24.
                        Second, by multiplying by 3, we reached a dead end because ((((((1 * 3) + 5) + 5) + 5) + 5) * 3) = 69 which is unfortunately already bigger than 24.
                        Sadly we've exhausted all possible ways of getting there from this starting point.  We may still be able to get to 24, but not by starting from (((((1 * 3) + 5) + 5) + 5) + 5).
                    Second, by multiplying by 3, we reached a dead end because (((((1 * 3) + 5) + 5) + 5) * 3) = 54 which is unfortunately already bigger than 24.
                    Sadly we've exhausted all possible ways of getting there from this starting point.  We may still be able to get to 24, but not by starting from ((((1 * 3) + 5) + 5) + 5).
                Second, by multiplying by 3, we reached a dead end because ((((1 * 3) + 5) + 5) * 3) = 39 which is unfortunately already bigger than 24.
                Sadly we've exhausted all possible ways of getting there from this starting point.  We may still be able to get to 24, but not by starting from (((1 * 3) + 5) + 5).
            Second, by multiplying by 3, and guess what...we finally found a solution! Because (((1 * 3) + 5) * 3) = 24.  So we can stop now. :)
(((1 * 3) + 5) * 3)

【讨论】:

  • 这超出了我所能要求的一切!我认为现在对我来说开始变得更好了。尤其是有关深度优先搜索和缩进 console.log 输出的信息。我仍然不是 100% 清楚为什么程序会以这种方式向前和向后运行,但我想我会看几个视频/阅读直到它在我脑海中根深蒂固哈哈。我现在有了更好的理解,感谢大家的帮助!这个社区很棒。
【解决方案2】:

只要你的current > target 返回null 并且你是find(current * 3,(${history} * 3)),就会被评估

假设

((((1 + 5) + 5) + 5) + 5)   ---> This is the current value of history

所以目前的当前值为21

现在当你到达​​p>

   find(current + 5, `(${history} + 5)`) ||
        find(current * 3, `(${history} * 3)`);

它调用 find(21 + 5, (${history} + 5))) 因为 current > target 的值所以从这个调用返回的值将是 null,因为 null 是假值所以第二个操作数将被调用,

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

所以这里的历史价值会

(((((1 + 5) + 5) + 5) + 5) * 3)

function findSolution(target) {
  function find(current, history) {
    if (current == target) {
      return history;
    } else if (current > target) {
      console.log(current, ' ---> ', `HISTORY AT NULL:  ${history}`);
      return null;

    } else {
      console.log(current,' ---> ', `${history}`);
      return find(current + 5, `(${history} + 5)`) ||
        find(current * 3, `(${history} * 3)`);
    }
  }
  return find(1, "1");
}
console.log(findSolution(8));

【讨论】:

  • 嘿,非常感谢您的意见!到目前为止我明白了..但是,当我看到这个时我迷路了..'HISTORY AT NULL: ((((((1 * 3) + 5) + 5) + 5) + 5) + 5)' ' NULL 时的历史: ((((((1 * 3) + 5) + 5) + 5) + 5) * 3)' ' NULL 时的历史: (((((1 * 3) + 5) + 5 ) + 5) * 3)' 'HISTORY AT NULL: ((((1 * 3) + 5) + 5) * 3)' .为什么当前 > 目标时“+5”被带走?
  • @Grant 尝试用一个小的值来理解它,看sn-p,这里的目标是8,所以第一次迭代它会是(1+5),它小于8,下一个值将是( 1 + 5 ) + 5) 这个值高于8 所以,|| 之后的函数将被调用,所以值将是(1 + 5) * 3,所以它返回 null 所以它回到我们传递( 1 + 5 ) 的部分并尝试传递( 1 * 3 ),然后在下一次迭代中传递( 1 * 3) + 5,它等于目标,因此它从那里返回
  • 我认为我的主要问题是你说“所以它回到我们通过的部分 (1 + 5) 并尝试通过 (1 * 3)”。我明白这就是它正在做的事情。我的问题是我试图在我的脑海中运行它的逻辑工作方式的迭代,我无法理解程序实际上是如何做到这一点的。无论如何,我真的很感谢你花时间帮助我理解!也许明天我的思路更清晰时,我需要重新审视它。
  • 我想我的主要困惑是:如果电流大于目标。那么这个程序不应该只是陷入返回 Null 并不断重启程序的无限循环,因为 Current 不会改变吗?也许如果我明白为什么不是这样,我会有更好的理解。再次,非常感谢!
猜你喜欢
  • 2020-11-27
  • 1970-01-01
  • 2019-04-15
  • 2016-02-02
  • 1970-01-01
  • 1970-01-01
  • 2011-12-10
  • 1970-01-01
相关资源
最近更新 更多