递归问题 - 希望获得针对我的代码的建议

Issue with Recursion - Hoping to get advice specific to my code

我过去曾尝试询问与此问题相关的建议,我被告知问题的根源是 "tail recursion"。人们将我引导到切线相关的帖子,但我无法将这些帖子中的建议应用到我的代码中。

有人可以解释我可以对我的代码进行哪些具体修改来解决我遇到的递归问题(详见下文)吗?我花了几个小时试图解决这个问题,但一无所获(这里是菜鸟...)。

那么,关于我的代码的一些背景:

这会生成 2 个介于 2 和 20 之间的不同随机数:

function GenerateRandomNumber1to20No1() {
    var min = 2, max = 20;
    var random = Math.floor(Math.random() * (max - min + 1)) + min;   
    return random;

}

 var GenerateRandomNumber1to20No1 = GenerateRandomNumber1to20No1(); 
$('.GenerateRandomNumber1to20No1').html(GenerateRandomNumber1to20No1);

 function GenerateRandomNumber1to20No2() {
    var min = 2, max = 20;
    var random = Math.floor(Math.random() * (max - min + 1)) + min;   
  return (random !== GenerateRandomNumber1to20No1) ? random: GenerateRandomNumber1to20No2(); 

  ///////////HERE'S ONE PLACE WHERE RECURSION IS AN ISSUE, I BELIEVE //////////////

        }

 var GenerateRandomNumber1to20No2 = GenerateRandomNumber1to20No2(); 
$('.GenerateRandomNumber1to20No2').html(GenerateRandomNumber1to20No2);

这会生成比前面 2 个数字少 2 个不同的数字:

function GenerateRandomNumber1to20lessthanNo1() {
    var min = 2, max = GenerateRandomNumber1to20No1-1;
    var random = Math.floor(Math.random() * (max - min + 1)) + 1;   
    return random;

}

 var GenerateRandomNumber1to20lessthanNo1= GenerateRandomNumber1to20lessthanNo1(); 
$('.GenerateRandomNumber1to20lessthanNo1').html(GenerateRandomNumber1to20lessthanNo1);

function GenerateRandomNumber1to20lessthanNo2() {
    var min = 2, max = (GenerateRandomNumber1to20No2 - 1);
    var random = Math.floor(Math.random() * (max - min + 1)) + min;   
  return (random !== GenerateRandomNumber1to20lessthanNo1) ? random: GenerateRandomNumber1to20lessthanNo2();

}    
///////////HERE'S ANOTHER PLACE WHERE RECURSION IS AN ISSUE, I BELIEVE //////////////


var GenerateRandomNumber1to20lessthanNo2 = GenerateRandomNumber1to20lessthanNo2(); 
$('.GenerateRandomNumber1to20lessthanNo2').html(GenerateRandomNumber1to20lessthanNo2);

我使用这些变量来替换 类 与其各自变量相对应的元素。

<span class = "GenerateRandomNumber1to20nNo2"></span>
<span class = "GenerateRandomNumber1to20nNo2"></span>
<span class = "GenerateRandomNumber1to20lessthanNo1"></span>
<span class = "GenerateRandomNumber1to20lessthanNo2"></span>

例如,<span class = "GenerateRandomNumber1to20nNo2"></span> 被替换为为变量 GenerateRandomNumber1to20nNo2 生成的数字。

有时代码工作正常:变量生成并且元素被这些变量替换。其他时候,变量不会填充,我会收到如下所示的错误:

Uncaught RangeError: Maximum call stack size exceeded
    at GenerateRandomNumber1to20lessthanNo2 

我被告知我收到此错误是因为我使用了 "tail recursion",但我无法将我对 "tail recursion" 的研究应用到我的代码中。我指出了代码中我认为递归存在问题的地方。

如果有人可以使用我的特定代码给我建议,我将不胜感激。

function GenerateRandomNumber1to20lessthanNo2() {
  var min = 2, max = (GenerateRandomNumber1to20No2 - 1);
  var random = Math.floor(Math.random() * (max - min + 1)) + min;   
  return (random !== GenerateRandomNumber1to20lessthanNo1)// <=== HERE
    ? random
    : GenerateRandomNumber1to20lessthanNo2();
}

想象一下您得到两个随机数的情况:2 和 3。 现在,您正在尝试滚动小于 2 和 3 的随机数,同时仍然使用 2 作为最小数字。

由于以下几个原因,您迷失了这些功能:

  • 您正在用函数 return... 的值覆盖函数,因此您的函数变成了值。这变得很难理解。
  • 你有 4 个函数,它们基本上做同样的事情,但测试不同的情况,这使得这部分很难推理,因为很容易眯着眼睛忘记你正在看的是哪个(它们是否仍然起作用) ,或者它们是您查看它们时的值)
  • 你有一些逻辑上可能的边缘情况,你没有测试(这就是你遇到无限递归的原因,也是你会遇到无限循环的原因)

我将很快用重构示例更新此内容。

function randomBetween (min, max) {
  var ceiling = max + 1;
  return Math.floor(Math.random() * (ceiling - min)) + min;
}

function randomBetweenAndExcluding (min, max, excluding) {
  var random;
  do {
    random = randomBetween(min, max);
  } while(random === excluding);
  return random;
}

var random1High = randomBetween(2, 20);
var random1Low = randomBetween(1, random1High - 1);
var random2High = randomBetweenAndExcluding(2, 20, random1High);
var random2Low = randomBetweenAndExcluding(1, random2High - 1, random1Low);

function addText (selector, text) {
  const el = document.querySelector(selector);
  if (el) { el.textContent = text; }
}

addText(".GenerateRandomNumber1to20nNo1", random1High);
addText(".GenerateRandomNumber1to20nNo2", random2High);
addText(".GenerateRandomNumber1to20lessthanNo1", random1Low);
addText(".GenerateRandomNumber1to20lessthanNo2", random2Low);
// PS you have a copy-paste error in your HTML classes, too
// the names are too long, and too similar, and already confused
// because of the overwriting functions with vars

对于此代码,random1Highrandom2Low 是相同的数字是完全可以接受的。如果那是不可接受的,那么如果掷出的数字是 2, 1, 3,您将向另一个无限循环将死敞开心扉。 如果那是不可接受的,那么你需要 运行 前两个数字在 3 到 20 之间随机,所以底部仍有 1 和 2 的空间,并使排除功能更漂亮。

希望在这里更容易看到移动的碎片。

如果你想像以前一样继续使用递归(一个函数从自身内部调用自身),你可以像这样定义第二个函数

function randomBetweenAndExcluding (min, max, exclude) {
  var random = randomBetween(min, max);
  return random === exclude
    ? randomBetweenAndExcluding(min, max, exclude)
    : random;
}

我通常更喜欢递归。递归的一个缺点是,如果在 returning 之前在太多函数中调用了太多函数(通常介于数万到数十万次调用之间,没有 returning,但仍然如此)。所以在无限递归的情况下,最终浏览器会抛出错误。 无限 while 循环的缺点是您的浏览器和页面会锁定,循环永远不会停止 运行ning,最终,您的浏览器可能会弹出一些内容并说 "Gee; this sure has been running a long time; it looks like the page died, would you like to kill it?" .