What are some good uses of the while statement? [closed] - javascript

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
Whenever I think of using the while loop, I don't see why I can't just use an if statement instead. I know that a while loop runs 'while' a given statement is true, but if the condition is terminated inside the block instantaneously, then I cannot visualize anything happening 'while' something is true. If anyone can give me examples of how/why I should use a while loop. It would be very appreciated.

I'm going to try to give you a very high-level answer because I think it will be easier to grasp intuitively.
Think of all algorithms as recipes. A typical recipe might have parts that look like the following:
If the peanut has a shell, separate it from the shell
Beat an egg until it is whipped and bubbly
Take 5 onions and chop each of them in half
These types of directions have you do tasks in different ways.
The first one is really an "if" statement. You do something if a condition (peanut has a shell) is true. You literally do:
if (not shelled) {
shell the peanut! // happens once
}
The second one is a while loop. You beat the egg while it is not whipped and bubbly. You do the same thing over and over (whipping) continuously, and you look at it (over and over) when doing this to check and see if it has turned very bubbly. Once it gets bubbly and whipped enough, you stop. When you stop it is because you have passed your "While" condition. An important thing to note is that before you start, you don't necessarily know just how many "beats" of an egg it takes to get it whipped. You just have to do it many times and check (with your eyes) many times. You literally do:
while(not whipped enough) {
beat egg! // happens over and over
}
Just like in recipes, you don't want someone doing a certain action until the end of time, so you must always make sure your while loops have sensible stopping conditions. Most while loops depend on something changing inside of the loop and will stop when that thing has changed enough.
The third recipe example is a for loop. For each onion you have (5) you do something once to that one onion. You literally do:
for (onions 1 to 5) {
Chop an onion! // done 5 times total, once per onion
}

while is a looping construct, not just another form of if
var i = 0;
while ( i < 10 ) {
//this stuff will happen until
//i < 10
// usually you iterate the counter so it
// eventually breaks the loop because
// i < 10 is no longer true
i++;
}
this does the same thing as this
for (var i = 0; i < 10; i++) {
//this stuff will happen until
//i < 10
}
Good use case for while
generally a for loop is used when the number of iterations that need to take place is known, so generally when you're iterating over an array and you know the length of the array for is a good choice because you can set the amount of iterations explicitly.
a better use case for while is when the amount of iterations is not static (known) and/or you are waiting for an event to occur to break the loop.

while will keep running until the condition becomes false.
If the condition is still true at the end of the block, the loop will run all over again.
if will only run once.

Well an if statement will not loop back around. It will only ever happen once unless you put it in a loop. A while statement will happen more than once as long as the statement is still true.

A while loop will execute over and over as long as the conditions are met, being that it will go to infinity if the condition never changes.
An if will execute once when the condition is met

If you wanted to create an infinite loop a while loop will do the trick
var done=false;
while(!done){
alert("I am annoying");
}

The for statement can be simulated with a while:
for(var i=0; i<length; i++) {}
// is the same as
var i=0;
while(i<length) {
// stuff
i++;
}
It is also used for loops like:
while(true) {
// stuff... (like waiting for user input, analyzing some data until
// it hits a brick wall and needs to quit)
if(some_condition) {
break;
}
}
Or for other stuff:
while(a != b) {
if(a > b) {
a -= b;
} else {
b -= a;
}
} // find the GCD of a and b
As you can see, it's very different from if, and solves other problems.

Related

Why cant I scroll browser window in a loop in console?

I want to scroll down the browser window in a loop in console. I want it so that every time a scroll down (x)px down I stop, do something and then scroll (x)px again and then stop etc. until the page with ends (its a very long one, I want to parse info from it).
But when I started I stumbled upon an issue, that the scrolling function is executed only once, after the loop ends.
let i = 0;
scrollDownTillEnd = () => {
for(i; i<100; i++) {
window.scrollBy(0, 1000);
};
scrollDownTillEnd();
(it is a simplified example, but the idea should be clear)
I put the code in the console, being on a page I want to scroll, and get then the value of i at the end of the loop and only one scroll down.
Please, explain me, why this piece of code behaves like this and how to make it work, as I mentioned before (in every loop it scrolls a little bit).
Thank you!
Let me help address a few issues going on here.
1) You have an infinite loop going on because you are not checking that i is less than 100 even though you are incrementing it each time. You need to check that i < 100 so that the loop will eventually end. In your loop, 0 will always be less than 100 so the loop will never end.
2) You have a syntax error in your example because you're not actually closing out the scrollDownTillEnd function with a curly brace before calling the function itself.
3) Lastly, as good practice, you need to reset your i variable to 0 each time so that we can run this piece of code over and over again. The way you have it set up in your example, since i will be equal to 100 at the end of the first run, the loop won't ever run again after that until you reset i to 0 again. The easiest way to do this is to just initialize i to a value of 0 each time you execute this loop.
Try something like this:
scrollDownTillEnd = () => {
for(let i = 0; i < 100; i++) {
window.scrollBy(0, 1000);
};
};
scrollDownTillEnd();
You can use setInterval() since for loop will executes only once
function scrollDownTillEnd(countryDropdownList)
{
let scrollingInterval = setInterval(function(){
window.scrollBy(0,1000)
// write your condition
if (window.scrollHeight > 10000)
{
clearInterval(scrollingInterval)
}
},100)
}
scrollDownTillEnd();

Testing/validating/evaluating the outcome of every path in a function?

Disclaimer - I've tried finding an answer to this via google/stackoverflow, but I don't know how to define the problem (I don't know the proper term)
I have many small AI snippets such as what follows. There is an ._ai snippet (like below) per enemy type, with one function next() which is called by the finite state machine in the main game loop (fyi: the next function doesn't get called every update iteration, only when the enemy is shifted from the queue).
The question: How do I test every case (taking into account some enemy AI snippets might be more complex, having cases that may occur 1 in 1000 turns) and ensure the code is valid?
In the example below, if I added the line blabla/1 under count++, the error might not crop for a long time, as the Javascript interpreter won't catch the error until it hits that particular path. In compiled languages, adding garbage such as blabla/1 would be caught at compile time.
// AI Snippet
this._ai = (function(commands){
var count = 0;
return {
next: function(onDone, goodies, baddies) {
// If the internal counter reaches
// 2, launch a super attack and
// reset the count
if(count >= 2) {
commands.super(onDone);
count = 0;
}
else {
// If not performing the super attack
// there is a 50% chance of calling
// the `attack` command
if(chance(50)) {
var target = goodies[0];
commands.attack(onDone, target);
}
// Or a 50% chance of calling the
// `charge` command
else {
commands.charge(onDone);
count++;
}
}
}
};
})(this._commands);
I could rig the random generator to return a table of values from 0-n and run next 1000's of times against each number. I just don't feel like that is will concretely tell me every path is error free.
As you say, unit tests must test every path so you will be sure all works well.
But you should be able to decide which path the method will follow before calling it on your tests, so you're be able to know if the method behaviour is the expected one, and if there is any error.
So, for example, if there is a path that will be followed in only one of every 1000 executions, you shouldn't need to test all 0, 1, 2 ... 999 cases. You only one combination of results that behave distinctly.
For example, in the snippet shown you have these cases:
the counter has reached 2
the counter has not reached 2 and chance returns true
the counter has not reached 2 and chance returns false
One way to archieve this is taking control of the counter and of the chance method by mocking them.
If you want to know what happens when the counter has reached 2 and the next method is called, just pass a counter with 2 and call next. You don't need to reach 2 on the counter by really passing for all the code.
As for the randomizer, you don't need to try until the randomizer returns the value you want to test. Make it a mock and configure it to behave as you need for each case.
I hope this helps.

not looping through all items in array

this.draw = function() {
console.log(this.buttonList.length);
for(a = 0; a < this.buttonList.length; a++) {
console.log(this.buttonList.length, a);
this.buttonList[a].draw();
}
};
So I have this function within an object, and it's not working the way I expected it to. When I run it with the above console.log statements, it logs this on the console:
2
2 0
This seems to tell me that my for loop is not looping through every item on the array, and I've been scratching my head over why that would be for a quite a while now. Does anyone know why it's only performing the action for a = 0?
edit: I don't know if this makes a difference, but this function is called about 60 times every second.
Adding var would probably fix it :
this.draw = function() {
console.log(this.buttonList.length);
for(var a = 0; a < this.buttonList.length; a++) {
console.log(this.buttonList.length, a);
this.buttonList[a].draw();
}
};
There's very probably another point of your code where you change a. You should be careful with the variable declarations.
There are atleast three possibilities for the behavior:
a is overwritten (as per dystroys answer).
All elements in buttonList don't have a draw function.
this in the function definition of draw is an element in the buttonList.
The first two possibilities are easy to fix but the third one depends on what your intentions are, i.e. what do you wish to accomplish. To fix it we need more information.
Any one of the three possibilities (or combinations of the possibilities) could account for the behavior.
The first possibility is explained by dystroy.
The second possibility will stop excecution if an element doesn't have a draw function. In this case it seems to be the first element.
The third possibility will render in a stack overflow due to infinite recursion. The draw function is called over and over again just logging to console 2 then 2 0 until all the stack is consumed at which point the execution stops.

Improve performance in js `for` loop

So I'm looking for some advice on the best method for toggling the class (set of three) of an element in a loop ending at 360 iterations. I'm trying to avoid nested loops, and ensure good performance.
What I have:
// jQuery flavour js
// vars
var framesCount = '360'; // total frames
var framesInterval = '5000'; // interval
var statesCount = 3; // number of states
var statesCountSplit = framesInterval/statesCount;
var $scene = $('#scene');
var $counter = $scene.find('.counter');
// An early brain dump
for (f = 1; f < framesCount; f += 1) {
$counter.text(f);
for (i = 1; i < statesCount; i += 1) {
setTimeout(function() {
$scene.removeClass().addClass('state-'+i);
}, statesCountSplit);
}
}
So you see for each of 360 frames there are three class switchouts at intervals. Although I haven't tested I'm concerned for the performance hit here once that frames value goes into the thousands (which it might).
This snippet is obviously flawed (very), please let me know what I can do to make this a) work, b) work efficiently. Thanks :-)
Some general advice:
1) Don't declare functions in a loop
Does this really need to be done in a setTimeout?
for (i = 1; i < statesCount; i += 1) {
setTimeout(function() {
$scene.removeClass().addClass('state-'+i);
}, statesCountSplit);
}
2) DOM operations are expensive
Is this really necessary? This will toggle so fast that you won't notice the counter going up. I don't understand the intent here, but it seems unnecessary.
$counter.text(f);
3) Don't optimize early
In your question, you stated that you haven't profiled the code in question. Currently, there's only about 1000 iterations, which shouldn't be that bad. DOM operations aren't too bad, as long as you aren't inserting/removing elements, and you're just modifying them.
I really wouldn't worry about performance at this point. There are other micro-optimizations you could apply (like changing the for loop into a decrementing while loop to save on a compare), but you gave no indication that the performance is a problem.
Closing thoughts
If I understand the logic correctly, you're code doesn't match it. The code will currently increment .counter as fast as the processor can iterate over your loops (should only take a few milliseconds or so for everything) and each of your "class switchouts" will fire 360 times within a few milliseconds of each other.
Fix your logic errors first, then worry about optimization if it becomes a problem.
Don't use a for loop for this. This will generate lots of setTimeout events which is known to slow browsers down. Use a single setTimeout instead:
function animate(framesCount, statesCount) {
$scene.removeClass().addClass('state-'+statesCount);
if (framesCount) {
setTimeout(
function(){
animate(framesCount-1,(statesCount%3)+1);
},
statesCountSplit
);
}
}
animate(360*3,1);

Disadvantages of While ! Undefined array loop

According to this JSPerf the While ! Undefined style array loop is ~10x faster in all browsers. What are the disadvantages of this style of loop?
It is actually a bug in the test case, the iterator is not being reset to zero for each test run (i.e., only the first test run rolls the full loop, next runs have the iterator already past the end of the array, thus roll zero times). See corrected test suite for the true benchmark.
(Note: I haven't inspected all test cases, others might be flawed as well)
As you can see, we're talking about 10x faster/slower on a scale of millions of operations per second, which is not significant enough to worry about.
A possible disadvantage of that style is readability to other developers, which is more important than the "performance boost".
Judge yourself, what's more readable?
// Chrome 21: 5M operations per second
var a;
while ((a = arr[i++]) !== undefined) {
someFn(a);
}
// Chrome 21: 324k operations per second
for (var i = 0; i < arr.length; i++) {
someFn(arr[i]);
}
The major disadvantage I can see is that you can't break out of the loop! You'll hit an unresponsive UI in no time with that.
disadvantages:
1. if a[i] has been used it is no longer undefined. Thus you may do more than anticipated.
2. readability, it is difficult to know the end point (unless you put some comments ;)
nothing else.
the new revision still doesn't differ that much, but if it's speed you need then comment well and test well.
If your function "someFn(a);" has a timer of more than in these tests then i'd recommend testing your own loop if it is that important.
If not always stick to tidy coding.

Categories