0

Error detailed towards bottom of post, but before getting to that, I'll give some background info. I have the following script, which generates:

1) TWO DIFFERENT NUMBERS BETWEEN 2 & 20

 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();

}

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

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

}

2) TWO DIFFERENT NUMBERS LESS THAN THE PREVIOUS 2 NUMBERS

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();

}

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

3) 2 DIFFERENT PRIME NUMBERS

    function PrimeNumber1() {
  var PrimeNumber1= ['3', '5', '7', '11'];
    var PrimeNumber1random= PrimeNumber1[Math.floor(Math.random() * PrimeNumber1.length)];
    return PrimeNumber1random;

}

var PrimeNumber1replacer= PrimeNumber1();
 $('.PrimeNumber1replacer').html(PrimeNumber1replacer);

    function PrimeNumber2() {
  var PrimeNumber2= ['3', '5', '7', '11'];
    var PrimeNumber2random= PrimeNumber2[Math.floor(Math.random() * PrimeNumber2.length)];
    return (PrimeNumber2random !== PrimeNumber1replacer) ? PrimeNumber2random: PrimeNumber2();

}

var PrimeNumber2replacer= PrimeNumber2();
 $('.PrimeNumber2replacer').html(PrimeNumber2replacer);

I USE THESE VARIABLES TO REPLACE ELEMENTS WITH CORRESPONDING CLASSES WITH THE VALUES OF THE RESPECTIVE VARIABLES

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

Sometimes, the code works fine: the variables generate and the elements are replaced with those variables. Other times, the variables don't populate and I get one of the two following errors:

Uncaught RangeError: Maximum call stack size exceeded
    at GenerateRandomNumber1to20lessthanNo2 *[or No1]*

OR

Uncaught TypeError: PrimeNumber2 *[or 1]* is not a function
    at PrimeNumber2

I tried to do some research on Stackoverflow and it seems it might be an issue with recursion, but I have no idea how to fix this issue. If anyone has any advice, I would appreciate it.

Thank you!

Snoops
  • 215
  • 1
  • 12
  • 2
    Use loops instead of recursion. – Barmar Dec 28 '17 at 23:01
  • I'm a complete novice, so unfortunately, I have no idea what that means. I'll certainly look it up, but are there any specific portions of my code that I should target / switch out? – Snoops Dec 28 '17 at 23:01
  • @Barmar Can you add an explanation for the OP as to why loops over recursion in this case? – mhodges Dec 28 '17 at 23:02
  • 3
    A bigger problem may be that you're using the same names for your variables and functions. That won't work, a name can only name one thing at a time. – Barmar Dec 28 '17 at 23:03
  • @Barmar Got it - that explains the error re: `PrimeNumber2`. If you could show me an example of how I might modify the `GenerateRandomNumber1to20lessthanNo2` code to change it to a loop, I would greatly appreciate it. – Snoops Dec 28 '17 at 23:04
  • Would also love if someone would post a solution using recursive tail-call optimization – mhodges Dec 28 '17 at 23:04
  • 1
    See https://stackoverflow.com/questions/8378870/generating-unique-random-numbers-integers-between-0-and-x/8378885#8378885 for how to generate N unique random numbers. Similar solutions can be used for the other problems. – Barmar Dec 28 '17 at 23:08
  • That helps, thank you. I just don't understand how I would integrate that solution with the portion of my code that uses those variables to replace elements of a particular class. (`var PrimeNumber2replacer= PrimeNumber2(); $('.PrimeNumber2replacer').html(PrimeNumber2replacer);` Using the response you sent as an example, how would I take the 3 random numbers generated and use them to replace elements in a similar way? – Snoops Dec 28 '17 at 23:18
  • @mhodges Neither Babel not any of the browsers are fully ES6 complete. I think only Safari can do it at this time. OPs code **is tail recursive** already. – Sylwester Dec 29 '17 at 00:44
  • @Sylwester Thank you for the feedback. I'll look into tail recursivity. Do you, by chance, have any recommendations as to how I can modify my code that it is not tail recursive? If you want to write it out as a response, I'll gladly hit accept on your answer. – Snoops Dec 29 '17 at 01:14
  • @Snoops you don't want to use recursion at all. I've added an answer. – Sylwester Dec 29 '17 at 01:30

1 Answers1

1

You get a stack overflow because almost none of the JS engines are ES6 compliant yet so even though you use tail recursion you blow the stack. The best thing right now is to rewrite it into a loop that does the same until you have succeded.

function generateRandomNumber(predicate = v => true) {
    const min = 2;
    const max = 20;
    let random;
    do {
        random = Math.floor(Math.random() * (max - min + 1)) + 1;
    } while (!predicate(random));
    return random;
}

// two different numbers
const first1 = generateRandomNumber();
const second1 = generateRandomNumber(v => v !== first1);

// two different number less than previous
const first2 = generateRandomNumber();
const second2 = generateRandomNumber(v => v < first2); // possible infinite loop

// two different prime numbers
function isPrime(n) {
  if (n % 2 === 0) return n == 2
  const limit = Math.sqrt(n);
  for (let i = 3; i <= limit; i += 2) {
    if (n % i === 0)
      return false;
  }
  return true;
}

const first3 = generateRandomNumber(isPrime);
const second3 = generateRandomNumber(v => isPrime(v) && v !== first3);

I've left out the code that puts the values onto the DOM since it's not very interesting. I don't name the variables after the function since they share the same namespace and thus after setting the name GenerateRandomNumber1to20No1 the function has been replaced with the value.

Note that i mention the "two different number less than previous" that you might get an infinite loop. There is a 5,5% chance that the first random number is 2. There is no number generated by that same function which is smaller than 2 and thus it will not terminate.

Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • Thank you so much for doing this. Just to make sure I'm understanding this correctly, if I add `var first2 = generateRandomNumber (); $('.first2 ').html(first2);` will that replace elements that have the class `first2` with the variable `first2`? – Snoops Dec 29 '17 at 01:47
  • @Snoops Not really. It will add the result as the body of the tags that have that class. so if you have `
    ` it becomes `
    5
    ` and not just `5`. Note that there is no requirement that the function name, the variable used and the string used as class needs to have the same name and you certainly don't need to use my chosen variable names either.
    – Sylwester Dec 29 '17 at 01:59
  • Does this need a node.js link? The code isn't working when I try it; I've never seen `const` before and when I looked it up, it appears as though it's for the node.js framework. – Snoops Dec 29 '17 at 02:08
  • @Snoops It's standard ES6. [`const`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const) and `let` are block scoped `var` and is supported for a long time so you need to have a very old browser for it not to work. Arrow functions are not supported in IE so I think perhaps it's that. `v => v !== first1` ==> `function(v) { return v !== first1; }` and `const`/`let` ==> `var` would work for the code in this answer. – Sylwester Dec 29 '17 at 11:24