【问题标题】:Maximum call stack size exceeded JS超过最大调用堆栈大小 JS
【发布时间】:2020-01-05 23:20:33
【问题描述】:

我正在尝试理解递归的概念并想在我的代码中使用它(getUniqueInt 函数):

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

var getChosenNumbers = function (min, max) {
  var chosenNumbers = [];
  for (var k = min; k <= max; k++) {
    chosenNumbers.push(k);
  }
  return chosenNumbers;
};

var arrayOfNumbers = getChosenNumbers(1, 8);

var getUniqueInt = function (min, max) {
  var uniqueNumber;
  var randomNumber = getRandomInt(min, max);
  if (arrayOfNumbers.indexOf(randomNumber) !== -1) {
    uniqueNumber = randomNumber;
    arrayOfNumbers.splice(arrayOfNumbers.indexOf(uniqueNumber), 1);
  } else {
    uniqueNumber = getUniqueInt(min, max);
  }
  return uniqueNumber;
};

但我最终得到了这个: 未捕获的 RangeError:超出最大调用堆栈大小

我做错了什么?我的代码(我的意思是递归部分)是否有意义或完全错误?

【问题讨论】:

  • 您的 sn-p 不会导致您描述的错误。 Uncaught ReferenceError: ADS_COUNT is not defined 并在那里插入一个号码看起来也不会导致问题。能不能编辑一下,当按下“Run code sn -p”时,显示的结果是“Uncaught RangeError: Maximum call stack size exceeded”,这样我们就可以看出问题了吗?

标签: javascript recursion


【解决方案1】:

但是我在这里基本上要做的是获取一系列数字(数字数组),然后随机选择其中一个数字,而不重复已经选择的数字;

getRandomInt 函数是一个好的开始 -

const getRandomInt = (min = 0, max = 0) =>
  Math.floor(Math.random() * (max - min)) + min

让我们创建一个函数来生成一个数字范围 -

const makeRange = (min = 0, max = 0) =>
  min > max
    ? []
    : [ min, ...makeRange(min + 1, max) ]

我们不必循环查找下一个随机值。我们可以swap 数组中的元素来有效地创建随机序列。这种技术被称为Fisher-Yates shuffle -

const getUniqueRandom = (min = 0, max = 0) =>
{ const r = makeRange(min, max)

  const next = (i = 0) =>
  { if (i >= r.length) return undefined
    swap(r, i, getRandomInt(i, r.length))
    return r[i]
  }

  let i = 0
  const rand = () =>
    next(i++)

  return rand
}

最后我们需要编写swap函数-

const swap = (a = [], i = 0, j = 0) =>
  [a[j], a[i]] = [a[i], a[j]]

现在是它的工作原理-

const rand = getUniqueRandom(3,7)

console.log(rand()) // 4
console.log(rand()) // 7

继续调用它以获取其余值。当没有可能的唯一输出时,它返回undefined -

console.log(rand()) // 3
console.log(rand()) // 6
console.log(rand()) // 5
console.log(rand()) // undefined

展开下面的 sn-p 以在您自己的浏览器中验证输出。按Run多次查看随机输出 -

const getRandomInt = (min = 0, max = 0) =>
  Math.floor(Math.random() * (max - min)) + min

const makeRange = (min = 0, max = 0) =>
  min > max
    ? []
    : [ min, ...makeRange(min + 1, max) ]
    
const swap = (a = [], i = 0, j = 0) =>
  [a[j], a[i]] = [a[i], a[j]]

const getUniqueRandom = (min, max) =>
{ const r = makeRange(min, max)
  
  const next = (i = 0) =>
  { if (i >= r.length)
      return undefined
    swap(r, i, getRandomInt(i, r.length))
    return r[i]
  }
  
  let i = 0
  const rand = () =>
    next(i++)
  
  return rand
}
      
const rand = getUniqueRandom(3,7)

console.log(rand()) // 4
console.log(rand()) // 7
console.log(rand()) // 3
console.log(rand()) // 6
console.log(rand()) // 5
console.log(rand()) // undefined

发电机

上面rand 演示的是某种生成器。现代 JavaScript 原生支持Generators,让我们可以方便地编写这个程序。您可能听说过他们用其他语言称为coroutines

这是一个非常简单的生成器,我们可以将其用于makeRange。注意使用yield 而不是return -

const makeRange = function* (min = 0, max = 0)
{ while (min <= max)
    yield min++
}

这是对getUniqueRandom 的重写。我们可以使用Array.from 收集来自makeRange(...) 的所有值 -

const getUniqueRandom = function* (min, max)
{ const r =
    Array.from(makeRange(min, max))

  for (let i = 0; i < r.length; i++)
  { swap(r, i, getRandomInt(i, r.length))
    yield r[i]
  }
}

逐一获取唯一的随机数 -

const rand = getUniqueRandom(3,7)

console.log(rand.next()) // { value: 7, done: false }
console.log(rand.next()) // { value: 3, done: false }

和以前一样,继续调用以获取下一个唯一随机数。当没有更多结果时,我们会看到 value: undefineddone: true -

console.log(rand.next()) // { value: 6, done: false }
console.log(rand.next()) // { value: 5, done: false }
console.log(rand.next()) // { value: 4, done: false }
console.log(rand.next()) // { value: undefined, done: true }

就像我们对 makeRange 所做的那样,如果我们想要立即获得所有结果,我们可以简单地使用 Array.from -

console.log(Array.from(getUniqueRandom(3, 7)))
// [ 6, 3, 4, 5, 7 ]

展开下面的 sn-p 以在您自己的浏览器中验证输出。按Run多次查看随机输出 -

const getRandomInt = (min = 0, max = 0) =>
  Math.floor(Math.random() * (max - min)) + min

const swap = (a = [], i = 0, j = 0) =>
  [a[j], a[i]] = [a[i], a[j]]
      
const makeRange = function* (min = 0, max = 0)
{ while (min <= max)
    yield min++
}

const getUniqueRandom = function* (min, max)
{ const r =
    Array.from(makeRange(min, max))

  for (let i = 0; i < r.length; i++)
  { swap(r, i, getRandomInt(i, r.length))
    yield r[i]
  }
}

const rand = getUniqueRandom(3,7)

console.log(rand.next()) // { value: 7, done: false }
console.log(rand.next()) // { value: 3, done: false }
console.log(rand.next()) // { value: 6, done: false }
console.log(rand.next()) // { value: 5, done: false }
console.log(rand.next()) // { value: 4, done: false }
console.log(rand.next()) // { value: undefined, done: true }

console.log(Array.from(getUniqueRandom(3, 7)))
// [ 6, 3, 4, 5, 7 ]

【讨论】:

  • 不客气。如果您在使用此答案时需要任何帮助,请告诉我:)
  • 对不起,你能详细说明一下交换吗?它是一个函数吗?我对“a = [], i = 0, j = 0”这一行有点困惑
  • 不道歉!是的,swap 是一个函数。我们可以写const swap = (a, i, j) =&gt; ...,但我写的是default argumentsa = [], i = 0, j = 0。这是我逐渐养成的习惯,因为它有助于将参数的 type 传达给读者;这意味着如果您忘记提供参数等,可以保留合理的输出。我们也可以编写其他类似的函数,例如const makeRange = (min = 0, max = 0) =&gt; ... - 我编辑了帖子以始终使用这种风格。
  • @MikeL。 JavaScript 支持 coroutines 在 JavaScript 中实现为 function*。我在更新的答案中添加了一个关于此的部分^^
  • 是什么让您选择Array.from(foo) 而不是[...foo]?我还没有找到更明确的呼叫和视觉噪音更少的呼叫之间的正确平衡。你有规矩吗?
【解决方案2】:

您的代码没有意义,抱歉,原因如下

  • 设置最小值=0,最大值=10

  • getRandomInt 返回 0-10 范围内的随机 int

  • getChosenNumbers 返回 int FROM 0-10 的数组,表示 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

  • arrayOfNumbers 现在是[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

getUniqueInt 根本无法获得 uniq,因为 getRandomInt 的所有可能随机值都已经在 getChosenNumbers

这就是为什么

 else {
    uniqueNumber = getUniqueInt(min, max);
  }

被无限次调用,因为

arrayOfNumbers.indexOf(randomNumber) !== -1

从来都不是真的

【讨论】:

  • 没问题,谢谢您的回答!但是我在这里基本上要做的是获取一系列数字(数字数组),然后随机选择其中一个数字,而不重复已经选择的数字;我该如何改进我的代码,有什么方法可以使用递归(这基本上只是为了我自己)?
猜你喜欢
  • 2021-02-05
  • 2020-08-10
  • 1970-01-01
  • 1970-01-01
  • 2017-02-04
  • 2014-08-19
  • 1970-01-01
  • 2013-08-19
  • 2015-10-24
相关资源
最近更新 更多