http://jsfiddle.net/goldrunt/jGL84/42/
this is from line 84 in this JS fiddle. There are 3 different effects which can be applied to the balls by uncommenting lines 141-146. The 'bounce' effect works as it should, but the 'asplode' effect does nothing. Should I include the 'shrink' function inside the asplode function?
// balls shrink and disappear if they touch
var shrink = function(p) {
for (var i = 0; i < 100; i++) {
p.radius -= 1;
}
function asplode(p) {
setInterval(shrink(p),100);
balls.splice(p, 1);
}
}
Your code has a few problems.
First, in your definition:
var shrink = function(p) {
for (var i = 0; i < 100; i++) {
p.radius -= 1;
}
function asplode(p) {
setInterval(shrink(p),100);
balls.splice(p, 1);
}
}
asplode is local to the scope inside shrink and therefore not accessible to the code in update where you are attempting to call it. JavaScript scope is function-based, so update cannot see asplode because it is not inside shrink. (In your console, you'll see an error like: Uncaught ReferenceError: asplode is not defined.)
You might first try instead moving asplode outside of shrink:
var shrink = function(p) {
for (var i = 0; i < 100; i++) {
p.radius -= 1;
}
}
function asplode(p) {
setInterval(shrink(p),100);
balls.splice(p, 1);
}
However, your code has several more problems that are outside the scope of this question:
setInterval expects a function. setInterval(shrink(p), 100) causes setInterval to get the return value of immediate-invoked shrink(p). You probably want
setInterval(function() { shrink(p) }, 100)
Your code for (var i = 0; i < 100; i++) { p.radius -= 1; } probably does not do what you think it does. This will immediately run the decrement operation 100 times, and then visually show the result. If you want to re-render the ball at each new size, you will need to perform each individual decrement inside a separate timing callback (like a setInterval operation).
.splice expects a numeric index, not an object. You can get the numeric index of an object with indexOf:
balls.splice(balls.indexOf(p), 1);
By the time your interval runs for the first time, the balls.splice statement has already happened (it happened about 100ms ago, to be exact). I assume that's not what you want. Instead, you should have a decrementing function that gets repeatedly called by setInterval and finally performs balls.splice(p,1) after p.radius == 0.
setInterval(shrink(p),100);
This doesn't do what you think it does. This calls shrink, passes it p, and then passes the result to setInterval. shrink(p) returns undefined, so this line doesn't actually put anything on an interval.
You probably want:
setInterval(function(){
shrink(p)
}, 100);
Related
I have got a homework where i am supposed to write a pseudo random number generator in JavaScript. This is the code bit i wrote
var k = 0;
var slump = function(n, k) {
if (k < 10) {
console.log("stop");
}
else {
k++;
console.log((5*n + 1) % 8);
return slump((5*n + 1) % 8, k);
}
};
slump(0);
k is supposed to hold the amount of times the function has been called. But instead of just running the function ten times, it just keeps running. Is there any way to get around this?
You have two subtly different options here, depending on how idiomatic and clever you'd like to get.
The classic implementation, with a slight tweak as JS doesn't support default parameters, would be to use something like:
var finalDepth = 0;
function slump(n, k) {
k = k || 0; // Set to 0 if falsy (null, undef, or 0)
if (logic) {
finalDepth = k; // Record the depth on the last call
} else {
return slump((5*n + 1) % 8, k + 1);
}
}
This will very simply record the deepest the stack has been, and hang onto the value until the next call.
If you want to be slightly more JS-like, you can use closure to keep track of the calls:
function createGenerator() {
var counter = 0;
return {
slump: function (n) {
++counter; // Closure captures counter, counter persists between slump calls but is unique for each createGenerator
if (logic) {
// stop
} else {
return slump((5*n + 1) % 8, k + 1);
}
},
getCounter: function () { return counter; }
}
}
You may be able to use some of the features from ES6 iterators (or generators) to make this more clever.
The function parameter k is uninitialized, therefore not a number. this means in particular that the termination test k < 10 fails as well as the k++ statement doesn't change k' s value. so slump gets always called with the same value for parameter k and the recursion never stops.
Whenever you write a recursive function, you need to make sure that:
There's a base case (in your case, when the console.log statement runs)
The function proceeds towards the base case, and
The function works, assuming the success of the recursive call.
You're running into a problem with the second part; you increment k, but that doesn't bring you any closer to the part where k < 10. In short, you probably want to switch that test around and make sure you're initially calling the function with the right number of arguments. (Aadit M Shah pointed out that you're calling it with one, and it expects two, meaning that it ends up undefined when you call it.)
Either way, iteration would definitely work better here:
var n = 0;
for(var i = 0; i < 10; i++) {
n = (5 * n + 1) % 8;
console.log(n);
}
I defined a variable 'i' to be equal to 2, and then say: if 'i' is between 0 and 2, let the function 'animate' run. However, if I open up the console in JSFiddle (option-command-I), the console.log() continues decreasing by 1 below 0! Am I using the conditional in the if statement improperly?
var interval = window.setInterval(animate, 500);
var i = 2;
if (0 < i < 2) {
function animate() {
alert('run');
i--;
console.log(i);
}
}
JSFiddle: http://jsfiddle.net/lpsternotes/RuLHn/
There are two problems here.
Firstly, there is this: 0<i<2. This will always evaluate to true.
Why? What you hoped it meant was "i is between 0 and 2" (which could be written as 0<i && i<2). But to a JS compiler, it is just two instances of the < operator. First, 0<i is evaluated, resulting in either true or false; then, that result is compared against <2 - so we have either true<2 or false<2. In order to answer that question, JS must "cast" the true or false to an integer. It treats false as 0, and true as 1; since these are both <2, the final result is always true.
Secondly, there is the position of your if statement, which is checked only once:
if (0 < i < 2) {
function animate() {
If you read through the code, you will see that there is no way of getting back to the line above this if statement, since the only repeating part of the code is the interval repeatedly running the animate function. To run each time the function runs, the if needs to be inside the function, not the other way around.
You need to clear interval once you satisfy the condition and move the if condition (note that 0 < i < 2 needs to be split into 2 conditions joined with && but here you can just do) with modification inside the function.
var interval = window.setInterval(animate, 500);
var i = 2;
function animate() {
if (i > 0) {
console.log('run');
i--;
console.log(i);
}
else{
window.clearInterval(interval);
}
}
Why don't you put if statement inside your animate function?
var i = 2;
function animate () {
if (i> 0 && i<2) {
alert('run');
i--;
console.log(i);
}
}
Edit:
1) you might want to use i>0 && i<2 rather than 0
2) Not sure if you do need to clear the interval after the condition is met. Would be better if you could elaborate a bit more about your requirements.
So I'm making a simple physics simulation using HTML5 canvas and javascript. I'm trying to make some realistic collisions, but whenever a collision occurs the code begins to loop infinitely and freezes the page.
I am using Google Chrome 24.0.1312.32 beta-m
When looking at the javascript console the line "console.log("I am colliding with something")" goes crazy and is printed thousands of times per second and completely breaks the page.
I'm not really sure why its' happening and I have no idea what to do. Any help and/or input would really be appreciated.
for (i = 0; i <= 3; i++) {
if (collide(i)) {
console.log("I am colliding with something");
if (typeof getCollideIndx === 'undefined') {
console.log("collide index is not undefined");
if (!getCollideIndx(i)) {
console.log("Made it past null check");
//...update object based on collision
the collide() function is:
function collide(b) {
for (i = 0; i <= 3; i++) {
//Distance between each object
var distance = (Math.sqrt(Math.pow((balls[b].x - balls[i].x), 2) + Math.pow(balls[b].y - balls[i].y, 2)));
if (distance < 32) {
//must be less than 2*radius -- all radii are the same
//makes it so that it doesn't return true when checking its own index
if (!(balls[b].mass == balls[i].mass)) {
return true;
} else {
return false;
}
}
}
}
I cannot see an infinite loop in your code , my best guess would be this statement
if (typeof getCollideIndx === 'undefined')
fails every time and whatever function the below code is in is called continuously
for (i = 0; i <= 3; i++) {
if (collide(i)) {
console.log("I am colliding with something");
if (typeof getCollideIndx === 'undefined') {
console.log("collide index is not undefined");
if (!getCollideIndx(i)) {
console.log("Made it past null check");
//...update object based on collision
also in this , I do not see the point of the for loop as the control always goes back to the calling function (in the collide() function)
Well, being trapped in that for loop implies that the index variable i is getting set incorrectly somewhere. Without being able to see the entirety of the code I cannot say for sure, but the loop for(i=0; i<3; i++){... will be assigning i to the window object because you have not explicitly scoped it (i.e. for(var i...).
See for example this jsfiddle where the first function will only run once rather than three times because the same variable i is affected in the second function.
Obviously running once != infinite loop, but if the collide function does something to i (or maybe breaks after it finds a collision?) then the value of i will be reset to 0 at the beginning of its for loop each time it's called.
So yeah without some more code I can't say for sure; but my advice in this case is: always use var in for loops or weird things can happen!
I guess its to stop browsers getting nailed all the time by duff code but this:
function print(item) {
document.getElementById('output').innerHTML =
document.getElementById('output').innerHTML
+ item + '<br />';
}
function recur(myInt) {
print(myInt);
if (int < 10) {
for (i = 0; i <= 1; i++) {
recur(myInt+1);
}
}
}
produces:
0
1
2
3
4
5
6
7
8
9
10
10
and not the big old mess I get when I do:
function recur(myInt) {
print(myInt);
if (int < 10) {
for (i = 0; i <= 1; i++) {
var x = myInt + 1;
setTimeout("recur("+x+")");
}
}
}
Am I missing something or is this how you do recursion in JS? I am interested in navigating trees using recursion where you need to call the method for each of the children.
You are using a global variable as loop counter, that's why it only loops completely for the innermost call. When you return from that call, the counter is already beyond the loop end for all the other loops.
If you make a local variable:
function recur(int) {
print(int);
if (int < 10) {
for (var i = 0; i <= 1; i++) {
recur(int + 1);
}
}
}
The output is the same number of items as when using a timeout. When you use the timeout, the global variable doesn't cause the same problem, because the recursive calls are queued up and executed later, when you have exited out of the loop.
I know what your doing wrong. Recursion in functions maintains a certain scope, so your iterator (i) is actually increasing in each scope every time the loop runs once.
function recur(int) {
print(int);
if (int < 10) {
for (var i = 0; i <= 1; i++) {
recur(int+1);
}
}
}
Note it is now 'var i = 0' this will stop your iterators from over-writing eachother. When you were setting a timeout, it was allowing the first loop to finish running before it ran the rest, it would also be running off the window object, which may remove the closure of the last iterator.
Recursion is very little restricted in JavaScript. Unless your trees are very deep, it should be fine. Most trees, even with millions of elements, are fairly wide, so you get at most log(n) recursive calls on the stack, which isn't noramally a problem. setTimeout is certainly not needed. As in your first example, you're right that sometimes you need a guard clause to guarantee that the recursion bottoms out.
Alright guys, I'm trying to add numbers on my page every 1/4 second or so. So the change is visible to the user. I'm using setTimeout and all my calculations are occurring correctly but without any delay. Here's the code:
for(var i = 0; i < 10; i++)
{
setTimeout(addNum(i),250);
}
I've also tried capturing the return value:
for(var i = 0; i < 10; i++)
{
var t = setTimeout(addNum(i),250);
}
I've also tried using function syntax as part of the setTimeout params:
for(var i = 0; i < 10; i++)
{
var t = setTimeout(function(){array[j].innerHTML + 1},250);
}
I've also tried putting code in a string & the function call in a string. I can't ever get it to delay. Help Please!
How about:
var i=0;
function adder() {
if(i>=10) {return;}
addNum(i++);
setTimeout(adder,250);
}
adder();
When you did setTimeout(addNum(i),250); you executed the function straight away (function name followed by () will execute it right away and pass the return value to the timeout to be executed 1/4 second later). So in a loop that would just execute all 10 immediately. Which is what you saw.
Capturing the return value var t = setTimeout(...); is helpful, but not in your use case; the value is the timer id number, used for cancelling the timeout.
Not sure what your last attempt is, although presumably it's the function body of your addNum routine, so the same logic applies as above.
Perhaps instead, since you're running the same method multiple times, you should use the setInterval method instead? Here's an example of how you might do that.
Try setTimeout("addNum(" + i + ")", 250); the reason its not working is because its evaluating the parameter and executing it and changing it to something like setTimeout(result of addNum(i), 250);