最大调用堆栈大小超过JS

时间:2019-09-03 04:36:42

标签: javascript recursion

我试图理解递归的概念,并想在我的代码(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:超出了最大调用堆栈大小

我在做什么错?我的代码(我的意思是递归部分)有意义吗?还是完全错误?

2 个答案:

答案 0 :(得分:1)

抱歉,您的代码没有意义,这就是原因

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

  • getRandomInt返回0-10之间的随机整数

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

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

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

之内

那就是为什么

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

被称为无限次,因为

arrayOfNumbers.indexOf(randomNumber) !== -1

永远不是真的

答案 1 :(得分: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

展开以下代码段,以在您自己的浏览器中验证输出。多次按 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的重写。我们可以使用makeRange(...)-

Array.from收集所有值
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 ]

展开以下代码段,以在您自己的浏览器中验证输出。多次按 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 ]