Array.push - “Maximum call stack size exceeded” из за простого Math.random(). Почему?

168
24 марта 2019, 14:20

Есть код, генерирующий массив из 10`000 случайных целых чисел от 0 до 1`000`000`000

let numbers = (() => { 
    let arr = [], n = 10000; 
 
    function generate(i) { 
        arr.push((Math.random() * 1000000000 | 0)); 
 
        if (i < n) { 
            generate(++i); 
        } 
    } 
 
    generate(1); 
 
    return arr; 
})(); 
 
console.log(numbers);

Хочу, чтобы в момент генерации случайного числа к каждому из них прибавлялось еще одно случайное число от 0 до 1, сгенерированное при помощи Math.random(), чтобы в итоге получилось число с плавающей точкой

let numbers = (() => { 
    let arr = [], n = 10000; 
 
    function generate(i) { 
        arr.push((Math.random() * 1000000000 | 0) + Math.random()); // изменения здесь 
 
        if (i < n) { 
            generate(++i); 
        } 
    } 
 
    generate(1); 
 
    return arr; 
})(); 
 
console.log(numbers);

Но на выходе получаю "RangeError: Maximum call stack size exceeded at Array.push".

Вопрос: почему так?

Интересно, что если прибавить вместо Math.random любое другое число от 0 до 1 (статичное), то проблема не возникает.

Заметил, что ".stack size exceeded at Array.push" иногда с неопределенной периодичностью сменяется на ".stack size exceeded at Math.random".

Answer 1

Math.random здесь ни при чем. При глубине вложенных вызовов 10000, любая лишняя локальная переменная имеет эффект в 10000 раз больше своего размера. Движок завел временную переменную под второй операнд - и все.

(То есть Math.random может быть "при чем" - как дополнительный вызов функции, требующий сохранения еще одного stackframe-а.)

С локальной переменной в рекурсивной функции:

let numbers = (() => { 
  let arr = [], 
    n = 10000; 
  //let b = 1; 
 
  function generate(i) { 
    let a = 2; //Math.random();// * 1000000000 | 0; 
    let b = 1; //Math.random(); 
    arr.push(a + b); // изменения здесь 
 
    if (i < n) { 
        generate(++i); 
    } 
  } 
 
  generate(1); 
 
  return arr; 
})(); 
 
console.log(numbers.length, numbers);

Без локальной переменной в рекурсивной функции:

let numbers = (() => { 
  let arr = [], 
    n = 10000; 
  let b = 1; 
 
  function generate(i) { 
    let a = 2; //Math.random();// * 1000000000 | 0; 
    //let b = 1; //Math.random(); 
    arr.push(a + b); // изменения здесь 
 
    if (i < n) { 
        generate(++i); 
    } 
  } 
 
  generate(1); 
 
  return arr; 
})(); 
 
console.log(numbers.length, numbers);

В разных браузерах - предел разный. Оба примерa у меня работают без ошибок в IE, Edge, Firefox. Чтобы продемонстрировать различное поведение, значение n в этих браузерах надо увеличить.

READ ALSO
Как можно объединить mysql запрос?

Как можно объединить mysql запрос?

Объединить 3 запроса в 1 возможно? А то сейчас приходится делать 3 через php

168
MYSQL Оптимизировать вложенный запрос с Group BY

MYSQL Оптимизировать вложенный запрос с Group BY

Ситуация следующая, есть БД MYSQL 55 в ней таблица с ~1млн строк

165
Почему округление до сотых при помощи ROUND() в MySQL округляет вниз?

Почему округление до сотых при помощи ROUND() в MySQL округляет вниз?

Выполняю запрос к БД MySQL, чтобы получить проценты за платеж:

165