Why doesn't += work in recursion? - javascript

I noticed if I use += in recursion it runs indefinitely because the variable being used in the termination statement doesn't increase in value. I can't figure out why, I've already looked on the forum for an answer but didn't see one. I'm trying to figure out if I'm doing something wrong and if not I'm looking for an answer on the specifics as to why it's not working.
//this works fine
function recure(n = 0) {
if (n > 10) {
console.log('The End');
return '';
}
console.log(n);
setTimeout(function () {
recure(n + 1)
}, 1000);
}
recure();
//this also works fine, note it's working with n+=1
function loop(amount = 10, n = 1) {
for (let i = 0; i < amount; i++) {
console.log(n);
n += 1;
}
}
//This doesn't work and is the reason for the post, why?
function recure(n = 0) {
if (n > 10) {
console.log('The End');
return '';
}
console.log(n);
n += 1;
setTimeout(function () {
recure()
}, 1000);
}
recure(); //it indefinitely logs 0

n is local to the particular call of a function. Since you call your function without arguments it will bind the new n gets bound to 0.
function test(n=0){
return n;
}
test(); //==> 0
test(2); //==> 2
Recursion isn't really treated special so if you assume that n is kept between calls to recure this should also happen:
function other(n=0) {
return n;
}
function test(n=0) {
n++;
return other();
}
test(0); // ==> 1
However this is absurd since n is local and thus every call have it's won n even if the function is the same.
Also note that the third example is not recursion since the call stack gets reset.
The anonymous function where you call recure has n in it's lexical scope so you can just call it with n+1 so the new recure will get a n that is one higher than the call the thunk derived from.

In JavaScript scope of variables is defined by functions. So, when you are calling the function recure() a new scope is getting created with new "N" in each scope.
Let's try to understand what you are doing here:
1. You called recure().
// No argument hence "N" is undefined therefore N is assigned 0.
2. You incremented value of "N"
// New value of "N" = 1
3. You recursively called recure()
// Again no argument hence "N" is 0
// Since, recure() is called again new scope is created
// This "N" is different from the "N" used is step 1
4. Bad recursion --> #Fail.

//this works fine
function recure(n = 0){
if (n > 10) {
console.log('The End');
return '';
}
console.log(n);
setTimeout(function() {
recure(n + 1)
}, 1000);
}
recure();
//this also works fine, note it's working with n+=1
function loop(amount = 10, n = 1) {
for (let i = 0; i < amount; i++) {
console.log(n);
n += 1;
}
}
//This doesn't work and is the reason for the post, why?
function recure(n = 0) {
if (n > 10) {
console.log('The End');
return '';
}
console.log(n);
n += 1;
setTimeout(function(){recure(n)},1000);
}
recure(); //it indefinitely logs 0
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

This should work
function recure(n=0){
if(n>10){
console.log("The End");
return "";
}
console.log(n);
n+=1;
setTimeout(function(){recure(n)},1000);
}
recure();
At every stage of recursion, that particular closure should know the previous recursion state.
We are passing 'n' to know the state of recursion.

Related

Higher-order function

I have an exercise about JavaScript. This exercise requires me to use higher-order functions. I have managed to specify some of the functions so far, but when I try to execute the code, the result does not seem to work properly. I have some images to give you an idea, hopefully, you can help me correct this.
The thread is: Write the function loop(loops, number, func), which runs the given function the given number of times. Also write the simple functions halve() and square().
This is my code:
function loop(loops, number, func) {
var loops = function(n) {
for (var i = 0; i < n; i++) {
if (i < 0) {
console.log('Programme ended')
}
if (i > 0) {
return n;
}
}
}
}
var halve = function(n) {
return n / 2
}
var square = function(n) {
return n ** 2;
}
console.log(halve(50));
console.log(loop(5, 200, halve));
console.log(loop(3, 5, square));
console.log(loop(-1, 99, halve));
Your current loop function declares an inner function and then exits. Ie, nothing actually happens -
function loop(loops,number,func){
// declare loops function
var loops= function(n){
// ...
}
// exit `loop` function
}
One such fix might be to run the supplied func a number of times in a for loop, like #code_monk suggest. Another option would be to use recursion -
function loop (count, input, func) {
if (count <= 0)
return input
else
return loop(count - 1, func(input), func)
}
function times10 (num) {
return num * 10
}
console.log(loop(3, 5, times10))
// 5000
so first things first: Higher-Order functions are functions that work on other functions.
The reason why you get undefined is because you are calling a function which doesn't return anything.
function x(parameter){
result = parameter + 1;
}
// -> returns undefined every time
console.log(x(5));
// -> undefined
function y(parameter){
return parameter+1;
}
// -> returns a value that can be used later, for example in console.log
console.log(y(5));
// -> 6
Second, you are using n for your for loop when you should probably use loops so it does the intended code as many times as "loops" indicates instead of the number you insert (i.e. 200, 5, 99).
By having the "console.log" inside a loop you may get a lot of undesired "programme ended" in your output so in my version I kept it out of the loop.
The other two answers given are pretty complete I believe but if you want to keep the for loop here goes:
function loop(loops, number, func){
if(loops>0){
for(let i = 0; i< loops; i++){ // let and const are the new ES6 bindings (instead of var)
number = func(number)
}
return number
}
else{
return "Programme ended"
}
}
function halve(n) { // maybe it's just me but using function declarations feels cleaner
return n / 2;
}
function square(n) {
return n ** 2;
}
console.log(halve(50));
console.log(loop(5, 200, halve));
console.log(loop(3, 5, square));
console.log(loop(-1, 99, halve));
Here's one way
const loop = (loops, n, fn) => {
for (let i=0; i<loops; i++) {
console.log( fn(n) );
}
};
const halve = (n) => {
return n / 2;
};
const square = (n) => {
return n ** 2;
};
loop(2,3,halve);
loop(4,5,square);

Invoke a function with is faster than directly invoking?

I tested performance with a script from "Secrets of Javascript Ninja":
function isPrime(number) {
if (number < 2) {
return false;
}
for (let i = 2; i < number; i++) {
if (number % i === 0) {
return false;
}
}
return true;
}
console.time("isPrime");
isPrime(1299827);
console.timeEnd("isPrime");
console.time("isPrime");
isPrime.apply(1299827);
console.timeEnd("isPrime");
And the result is:
isPrime: 8.276ms
isPrime: 0.779ms
Seems that "apply" is faster?
Your comparison is not accurate, because the first parameter passed to apply is the this value of the called function, and the second parameter passed to apply is an array of parameters that function is to be called with. So, your apply is not calling isPrime with any parameters, so no iterations run, because the condition i < number is not fulfilled when i is 2 and number is undefined:
function isPrime(number) {
console.log('calling with ' + number);
if (number < 2) {
return false;
}
for (let i = 2; i < number; i++) {
if (number % i === 0) {
return false;
}
}
return true;
}
console.time("isPrime");
isPrime(1299827);
console.timeEnd("isPrime");
console.time("isPrime");
isPrime.apply(1299827);
console.timeEnd("isPrime");
If you use apply properly and pass in undefined, [1299827], the result is as expected, very similar. You should also use performance.now() for better precision than console at the millisecond level, though for such a quick operation you might not see that might difference anyway:
function isPrime(number){
console.log('calling with ' + number);
if(number < 2) { return false; }
for(let i = 2; i < number; i++) {
if(number % i === 0) { return false; }
}
return true;
}
const t1 = performance.now();
isPrime(1299827);
const t2 = performance.now();
isPrime.apply(undefined, [1299827]);
console.timeEnd("isPrime");
const t3 = performance.now();
console.log(t2 - t1);
console.log(t3 - t2);
The syntax for .apply is
function.apply(thisArg, [argsArray])
the first parameter thisArg refers to the value of 'this' when calling the function, in your case isPrime.apply(1299827) you passed in 1299827 as 'this' but no parameter, so it's really isPrime(), the for loop is not excuted so it's faster
more on .apply here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
You have to read this.
reference: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
This point is Array.prototype.apply(context = this, args = []), so your code is wrong.
Change your code to this.
// incorrect.
isPrime.apply(1299827);
// correct.
isPrime.apply(this, 1299827);

Function that counts how often it call another function

I have a 'twice' function that return 2 of the argument passed into it. I also have another function 'runTwice' that counts the number of times it called the 'twice' function (the idea being that I want the 'twice' function to only run 'twice' no matter how often it is called via the 'runTwice' function). Can you please help?
Functions are given below:
var count = 1;
function twice(num){
return num*2;
}
function runTwice(func){
if (count<3){
count++;
return func;
} else {
return 'Function cannot run!';
}
}
var myFunc = runTwice(twice)
var output = [];
for (var i = 0; i < 3; i++){
output.push(myFunc(i));
}
console.log(output);
I would like the output to be [0, 2, 'Function cannot run!'].
I can make this work if I count the 'twice' function directly but I am looking to understand why this doesn't work as presented above.
Just for fun I'll make a generic expireAfter(invocable[, times[, message]]) function:
function expireAfter(invocable, times = 2, message = 'Function cannot run!') {
return function expires() {
if (times > 0) {
times--;
return invocable.apply(this, arguments);
}
return message;
}
}
function twice(n) {
return n * 2;
}
var myFunc = expireAfter(twice);
console.log(Array(3)
.fill()
.map((_, index) => myFunc(index))
);
The function runTwice should return another function that will decide whether to call the function func (using Function.prototype.apply) or to return a string message instead:
function twice(num){
return num * 2;
}
function runTwice(func){
var count = 0; // this will be trapped in a closure along with func
return function() { // this is the function that gets called
count++; // it increments its version of the count variable
if(count <= 2) // if count is less than 2
return func.apply(this, arguments); // then it calls the function func with whatever arguments passed into it and return the returned value of that call
return "Not available anymore!"; // otherwise (count > 2), then it returns a string
}
}
var myFunc = runTwice(twice);
for (var i = 0; i < 3; i++){
console.log(myFunc(i));
}
Even better:
You can pass in the number of times allowed as well:
function double(num) {
return num * 2;
}
function triple(num) {
return num * 3;
}
function run(func, times){
var count = 0; // this will be trapped in a closure along with func and times
return function() { // this is the function that gets called
count++; // it increments its version of the count variable
if(count <= times) // if count is less than times
return func.apply(this, arguments); // then it calls the function func with whatever arguments passed into it and return the returned value of that call
return "Not available anymore!"; // otherwise (count > times), then it returns a string
}
}
var double2times = run(double, 2); // double2times can only be called 2 times
var triple5times = run(triple, 5); // triple5times can only be called 5 times
for (var i = 0; i < 10; i++){
console.log("Double:", double2times(i));
console.log("Triple:", triple5times(i));
}

fail to setTimeout in a for loop

I'm building a simon game. And after each round the player should see the moves he must play in the next round. So i created a function showMoves which flashes the square he has to play. The problem is that the function is not showing anything. Can anyone tell me what did i miss?
// the effect
function flasher(index) {
$(moves[index]).fadeIn(50).fadeOut(50).fadeIn(50).fadeOut(50).fadeIn(100);
}
var interval2;
// show the moves that supposed to be played
function showMoves() {
for (var i = 0; i < moves; i++) {
if (i === 0) {
interval2 = setTimeout(flasher(i), 1000);
} else {
interval2 = setTimeout(flasher(i), (i+1) * 1000);
}
}
}
setTimeout accepts a function as a first parameter. I assume that by calling flasher you tried to avoid this situation. In you case, this should be done like this:
function showMoves() {
for (var i = 0; i < moves; i++) {
if (i === 0) {
interval2 = setTimeout(function(i) {return function() {flasher(i)}}(i), 1000);
} else {
interval2 = setTimeout(function(i) {return function() {flasher(i)}}(i), (i+1) * 1000);
}
}
}
The setTimeout and setInterval are a little diffrent than we think about them.
They are save some event on specified times that will be fired in its times. Because of this they has a problem with loops:
for(i=0;i<3;i++)
{
setTimeout(function(){alert(i)}, i*1000);
}
after ending the loop the browser has 3 jobs to do:
alert(i) after 1 second
alert(i) after 2 seconds
alert(i) after 3 seconds
But what is the value of 'i'. If you are in c# programming after ending the loop 'i' will be disposed and we have not that.
But javascript does not dispose 'i' and we have it yet. So the browser set the current value for i that is 3. because when 'i' reaches to 3 loop goes end. Therefor Your browser do this:
alert(3) after 1 second
alert(3) after 2 seconds
alert(3) after 3 seconds
That is not what we want. But if change the above code to this:
for(i=0;i<3;i++){
(function (index)
{
setTimeout(function () { alert(index); }, i * 1000);
})(i);
}
We will have:
alert(0) after 1 second
alert(1) after 2 seconds
alert(2) after 3 seconds
So as Maximus said you mast make the browser to get value of i currently in loop. in this way:
setTimeout(function(i) {return function() {flasher(i)}}(i), (i+1) * 1000);
i does not leave out until end of loop and must be get value just now.
What I can derive from your code is that moves is an array, but you're using it as if it's an integer in the for loop. And that's why nothing happens at all.
Replace:
for (var i = 0; i < moves; i++) {
With:
for (var i = 0; i < moves.length; i++) {
And you should see things happening.
But you will notice flasher is called immediately, without timeout. And that's because the result of flasher is set to be called, instead of flasher itself.
Other answers here suggest using an wrapper function, but this requires workarounds to correctly pass the index to the function called by setTimeout.
So assuming that it doesn't have to run in IE8 and below, the following is the most concise solution:
setTimeout(flasher.bind(null, i), (i+1) * 1000)
Full working example:
var moves = [1, 2, 3, 4];
function flasher(index) {
console.log('move', moves[index]);
}
var interval2;
// show the moves that supposed to be played
function showMoves() {
for (var i = 0; i < moves.length; i++) {
interval2 = setTimeout(flasher.bind(null, i), (i+1) * 1000);
}
}
showMoves()

Create a JavaScript loop without while

I am trying to create a page which needs to preform lots of loops. Using a while/for loops cause the page to hang until the loop completes and it is possible in this case that the loop could be running for hours. I have also tried using setTimeout, but that hits a recursion limit. How do I prevent the page from reaching a recursion limit?
var looper = {
characters: 'abcdefghijklmnopqrstuvwxyz',
current: [0],
target: '',
max: 25,
setHash: function(hash) {
this.target = hash;
this.max = this.characters.length;
},
getString: function() {
string = '';
for (letter in this.current) {
string += this.characters[this.current[letter]];
}
return string;
},
hash: function() {
return Sha1.hash(this.getString());
},
increment: function() {
this.current[0] += 1;
if (this.current[0] > this.max) {
if (this.current.length == 1) {
this.current = [0, 0];
} else {
this.current[1] += 1;
this.current[0] = 0;
}
}
if (this.current[1] > this.max) {
if (this.current.length == 2) {
this.current[2] == 0;
} else {
this.current[3] += 1;
this.current[2] = 0;
}
}
},
loop: function() {
if (this.hash() == this.target) {
alert(this.getString());
} else {
this.increment();
setTimeout(this.loop(), 1);
}
}
}
setInterval is the usual way, but you could also try web workers, which would be a more straightforward refactoring of your code than setInterval but would only work on HTML5 browsers.
http://dev.w3.org/html5/workers/
Your setTimeout is not doing what you think it's doing. Here's what it's doing:
It encounters this statement:
setTimeout(this.loop(), 1);
It evaluates the first argument to setTimeout, this.loop(). It calls loop right there; it does not wait for a millisecond as you likely expected.
It calls setTimeout like this:
setTimeout(undefined, 1);
In theory, anyway. In reality, the second step never completes; it recurses indefinitely. What you need to do is pass a reference to the function rather than the returned value of the function:
setTimeout(this.loop, 1);
However, then this will be window on the next loop, not looper. Bind it, instead:
setTimeout(this.loop.bind(this), 1);
setInterval might work. It calls a function every certain amount of milliseconds.
For Example
myInterval = setInterval(myFunction,5000);
That will call your function (myFunction) every 5 seconds.
why not have a loop checker using setInterval?
var loopWorking = false;
function looper(){
loopWorking = true;
//Do stuff
loopWorking = false;
}
function checkLooper()
{
if(loopWorking == false)
looper();
}
setInterval(checkLooper, 100); //every 100ms or lower. Can reduce down to 1ms
If you want to avoid recursion then don't call this.loop() from inside of this.loop(). Instead use window.setInterval() to call the loop repeatedly.
I had to hand-code continuation passing style in google-code prettify.
Basically I turned
for (var i = 0, n = arr.length; i < n; ++i) {
processItem(i);
}
done();
into
var i = 0, n = arr.length;
function work() {
var t0 = +new Date;
while (i < n) {
processItem(i);
++i;
if (new Date - t0 > 100) {
setTimeout(work, 250);
return;
}
}
done();
}
work();
which doesn't hit any recursion limit since there are no recursive function calls.

Categories