...

View Full Version : switch vs. if else



xelawho
08-30-2011, 11:08 PM
just a general question really... now that I've finally started using switches, it seems to me that they're just a big if /else if statement, with an else thrown in at the end for the default.

but that can't be right. I know that in javascript there are a million ways to achieve the same result, but why would they (whoever they are) go to the trouble of making two basic operations that do exactly the same thing?

to me it seems that switches are better for lots of conditions and if else is a quick way to work with two or three, but apart from readability, are there any inherent advantages in choosing one approach over the other?

all opinions appreciated, and I hope I don't start another argument.

devnull69
08-30-2011, 11:15 PM
Switch and if/else if are not doing exactly the same thing. If/else if is much more flexible and switch is much easier to use if the compared expression is the same

Example of if/else if that cannot (easily) be converted into switch


if(a==5) {
...
} else if (b==7) {
...
}

oracleguy
08-30-2011, 11:17 PM
A switch statement can be much faster in some cases because it will be turned into a jump table. So instead of having to evaluate a number of conditions until one is true, the matching condition can be jumped to immediately.

rnd me
08-30-2011, 11:29 PM
Example of if/else if that cannot (easily) be converted into switch


if(a==5) {
...
} else if (b==7) {
...
}


BS!
since case operators accept an expression (though folks typically just a static primitive), just overload the switch:

switch(true) {
case a==5: break;
case b==7: break;
default: break;
}

now you can do several comparisons mid-switch... pretty cool huh? keep it on the downlow.


personally, for one-switch-one expressions, i prefer look up tables, which are MUCH faster for those simple one determine another things:



var myLetter="c";
var myPhonic={
a: "aye",
b: "bee",
c: "see"
}[myLetter];

//myPhonic=="see"...

Old Pedant
08-30-2011, 11:29 PM
Simple answer: switch can only be used to test a single value and thus a single condition.
It is much much more efficient than if...else if...else if... but it's also much more limited.

Use it if it works for the situation. Don't if it doesn't.

Old Pedant
08-30-2011, 11:31 PM
And I admit (shamelessly) to having used RndMe's trick. But I think it's a bad coding style simply because it makes it much less clear what you are trying to accomplish. Pity the poor neophyte who is assigned to maintaining your code 3 years after you have left the company. He would want to shoot you.

rnd me
08-30-2011, 11:37 PM
A switch statement can be much faster in some cases because it will be turned into a jump table. So instead of having to evaluate a number of conditions until one is true, the matching condition can be jumped to immediately.

that would violate the spec. each case is evaluated until a true expression is found, and which point all subsequent cases are executed until a break is encountered.

example:



var x=2;
var out="blank";

switch(x){
case 1: out="one";
case 2: out="two";
case 3: out="three"; break;
default: out="defaulted";
}

alert(out); //shows: "three" (!@#$*)

which reminds me, watch those breaks everyone!

Old Pedant
08-30-2011, 11:45 PM
Yeah, JS's version of switch is uglier than C/C++/Java. In those languages, the case *must* be a constant value, not an expression. The result is that, indeed, the compiler can build a jump table that can be enormously efficient.

Unless a JS compiler is smart enough to realize that all the case values are constants and then do the same (build a jump table), it has no real choice but to evaluate the case expressions at run time, so a lot of performance advantage over if..else is lost. But who knows: Maybe modern JS compilers *are* that good.

oracleguy
08-30-2011, 11:50 PM
that would violate the spec. each case is evaluated until a true expression is found, and which point all subsequent cases are executed until a break is encountered.

Well in the case of languages that don't allow you to do have evaluations on each case statement, a switch is incredibly more efficient (when checking against a constant value). I didn't know JS let you do that. It seems like a strange feature to support. Hopefully the JS compiler uses a jump table when the switch is only evaluating a constant value so if you use a switch traditionally (dare I say correctly), you get the speed improvement.

xelawho
08-31-2011, 02:06 PM
interesting. actually more interesting than I'd anticipated, so thank you all.

A couple of clarifications (and I am googling this, too, but it's obviously nice to see a dynamic discussion rather than isolated opinions)...

Old Pedant, I'm assuming that when you talk about rnd me's 'trick' you're talking about this one:

switch(true) {
case a==5: break;
case b==7: break;
default: break;
}

but it seems to me to be as readable/comprehensible as any of the other methods... can you maybe give a more "real-lifeish" example of how using this approach will result in code that would be hard to understand/modify by someone else in the future?

and, thinking about it more, isn't


if(a==5) {
...
} else if (b==7) {
...
}

really two separate if statements, ie it would be exactly the same to use


if(a==5) {
...
} if (b==7) {
...
}

meaning that really it could, for some reason, be done as



switch(a) {
case 5: ...;
break; // although I guess with only one condition to check the break is not strictly necessary here?
}
switch(b) {
case 7: ...;
break; // ditto
}

the other one was minor - I see we're talking about jump tables and look up tables (which I hadn't heard of before, but seem to be interesting, too)... are these the same thing, just with different names and if so, is that the method described as "Lookup by object" here (http://jsperf.com/casevsswitch/5) and (sorry, but my curiosity has been piqued) being that the "Lookup by array" method comes out more or less the same in terms of speed, are there any substantive differences between those two approaches?

thanks again for your thoughts.

rnd me
08-31-2011, 08:52 PM
the other one was minor - I see we're talking about jump tables and look up tables (which I hadn't heard of before, but seem to be interesting, too)... are these the same thing, just with different names and if so, is that the method described as "Lookup by object" here (http://jsperf.com/casevsswitch/5) and (sorry, but my curiosity has been piqued) being that the "Lookup by array" method comes out more or less the same in terms of speed, are there any substantive differences between those two approaches?

thanks again for your thoughts.

jump table is likely a term for an internally optimization done during compilation time. look up table is the same as "lookup by object" in your link'd page. what they call "Lookup by array" is actually just mis-using the array as an object, so yeah, they are comparable. if you used the array properly (with numerical keys), you would have to loop through all the elements in an iteration procedure like for or indexOf (js1.6/es5).


if you only need to determine one thing based on a list of possible other things, the look-up table pattern (y={a:1,b:2}[x]) is going to be the fastest, especially when comparing more than a dozen choices.

Old Pedant
08-31-2011, 09:40 PM
What rndme said. In spaces.

Additionally:

*NO*. Doing


if(a==5) {
...block1...
} if (b==7) {
...block2...
}

is *IN NO WAY* equivalent to doing

if(a==5) {
...block1...
} else if (b==7) {
...block2...
}

Let's say that indeed a *IS* equal to 5 and b *IS* equal to 7.

In the first code, *BOTH* if conditions are true and *BOTH* blocks of code WILL be executed.

With the else in place, block2 will NEVER be executed if block1 is executed.

And the same is true with your dual switch code.

switch(a) {
case 5: ...block1...;
break;
}
switch(b) {
case 7: ...block2...;
break;
}

If a is 5 and b is 7, *BOTH* blocks will be executed.

With

switch(true) {
case a==5: ...block1...; break;
case b==7: ...block2...; break;
default: ...block3...; break;
}
only *ONE* of the blocks will be executed.

DO NOT TAKE SHORTCUTS if you don't understand the consequences!

rnd me
08-31-2011, 10:31 PM
personally, i almost never use "else if" for readability reasons. once i get a nest or two deep, i find else and a bunch of "}}}"s to be confusing.

for me, and maybe i'm just dumb (a possibility for sure), i would code it the dummy-proof way:


if(a==5) {
...
} else {
if (b==7) {... }
}

but again, that's just me.

there's a ton of way to code this sequence, use whatever feels right for you.

another fool-proof way:


if(a==7){ ... }
if(a!=7 && b==7){ ... }

Old Pedant
08-31-2011, 11:11 PM
Ummm...RndMe, you do *NOT* need or get a bunch of "}}}"s if you use if...else correctly.



if ( a == 5 )
{
foo = "bananas";
bar = "apples";
} else if( b == 7 ) {
foo = "godzilla";
zam = "magilla";
} else if ( c == 971281 ) {
bar = "howzit";
zam = "hangin";
}

And so on.

Coded like that, you will never have more than one pair of {...} at a time to worry about.

Your supposed dummy-proof way is the one that gets you into trouble with counting }'s.


if(a==5) {
...
} else {
if (b==7) {
...
} else {
if ( c == 9 ) {
...
} else {
if ( d > 7 ) {
...
}
}
}
}

Ugh, to say the least.

rnd me
09-01-2011, 12:35 AM
yeah, i guess that is cleaner...

maybe it's just my coding style, but i rarely use ifs, and never use else if.
i think since i do so much functional, i use return instead of else/else if.

to some purists, a function should have only one return, but i find it works like a charm on filters and even maps. i guess .map replaces for, and return replaces else.
i would do it the simple way, but i gotta have my private iteration scope...


thanks for writing up a coherent example of else if, i might add that to my toolkit. having never had a formal CS education, i've had to feel my way around in the dark on some of these very basic concepts. while i've done alright for myself, it does lead to some "duh" moments every now and then. this is one of them. thanks again.

xelawho
09-01-2011, 06:13 PM
With the else in place, block2 will NEVER be executed if block1 is executed.


no, of course it won't - that was a silly question, sorry.

here's a possibly less-silly one...

my vague understanding of why switches are faster is that the code checks 'til it finds a condition that matches, then breaks permanently. But isn't this in effect what an if else does, if one condition meets the requirements and everything following is prefixed by "else" then surely the code will just see that "else" and not bother checking if the statement is true, because as you say, even if it is true that code will never get executed? So really, all it ends up doing is reading the "else's" until the end... which can't make it all that slower - or can it?

Old Pedant
09-01-2011, 06:59 PM
I don't think you followed our discussion of "jump tables".

What you say is probably true of switches where the case values are expressions:


switch (true)
{
case a > 5 : xxx ; break;
case b < 7 : yyy; break;
}

But when you use a traditional switch (that is, C/C++/Java style) you can't use an expression for the case value:


switch ( x )
{
case 5: xxx; break;
case 37: case 29: yyy; break;
...
}

That enables the compiler to build a jump table. *ESPECIALLY* if the case values are sequential (e.g., case 1: through case 12:, say) this is very very efficient.

The compiler simply builds an internal table. In JavaScript style, something like


var jump = {
5 : address_of_case_5,
37 : address_of_case_37_and_29,
37 : address_of_case_37_and_29
...
}

And then, at run-time, it can do a very quick lookup by value and jump directly to the code for the given case.

When the indexes are sequential, it doesn't even need a key/value table like that. It can just use an array:


var jump = [
address_of_case1,
address_of_case2,
address_of_case3,
...
address_of_case12
];

And then it simply looks to see (a) is the switch value between 1 and 12? yes, look up the address by index (minus 1) in the jump array. (b) no, go to the default case.

Now...this kind of optimization is common--I'd say even de riguer--in C/C++/Java compilers. Whether or not a given JavaScript compiler/runtime uses this, I don't know. But I would suspect that advanced JS implementations do.

xelawho
09-01-2011, 07:28 PM
I don't think you followed our discussion of "jump tables".

well, I read it if that's what you mean, although I am reminded of that scene from A Fish Called Wanda...
Otto: Apes don't read philosophy.
Wanda: Yes they do, Otto. They just don't understand it.

but anyway... am I right in surmising from all this that the look up table mentioned by rnd me back in post #4:


var myLetter="c";
var myPhonic={
a: "aye",
b: "bee",
c: "see"
}[myLetter];

//myPhonic=="see"...

is quickest because it basically does the work of compiling what (in optimum circumstances) the js compiler is going to create when supplied with a switch anyway?

Old Pedant
09-01-2011, 07:40 PM
I would hesitate to say it will always be quickest.

Because 'a', 'b', 'c' are sequential values, a sophisticated compiler could *probably* convert

switch ( myLetter )
{
case 'a': result = "aye"; break;
case 'b': result = "bee"; break;
case 'c': result = "see"; break;
}

into the even more efficient form I mentioned: A simple array.


var jump = [ "aye", "bee", "see" ];

And then it would generate machine code equivalent to something like this:


if ( myLetter >= 'a' && myLetter <= 'c' ) result = jump[ myLetter.charCodeAt(0) - 'a'.charCodeAt(0) ];

That looks complicated, but in machine code terms it's maybe only 6 or 7 tiny little instructions (in, say, a Pentium CPU). Including the space for the jump table, maybe 40 to 60 bytes of memory. Miniscule. And extraordinarily fast.

[Yes, I used to write work on teams that wrote compilers. Wrote the code generator (that is, the part that generates the machine code) once upon a time. Mumblety-mumble years ago.]

Old Pedant
09-01-2011, 07:43 PM
By the by, even if you had coded


switch ( myLetter )
{
case 'a': result = "aye"; break;
case 'c': result = "see"; break;
case 'e': result = "eek"; break;
}

a sophisticated compiler would have treated that as "sequential" values.

By the simple expediency of making the array look like:


var jump = [ "aye", null, "see", null, "eek" ];

Because it would recognize that the additional space needed to plunk in a few null values would be more than made up for by the performance gain.

xelawho
09-01-2011, 11:21 PM
thanks for taking the time to explain all of that, Old Pedant. I don't know if it will ever be useful in a practical context, but it's interesting to know what's going on under the hood.

Old Pedant
09-01-2011, 11:43 PM
Realistically, JavaScript implementations differ *SO* much that it would be hard to make a performance judgment on a particular situation without simply benchmarking it on all platforms. So... don't worry about it.

xelawho
09-02-2011, 08:43 AM
cool. one less thing to worry about :thumbsup:

here's something that's vaguely related and actually practical that I have been wondering about. It takes a little explaining, but if you're not interested in that bit, just skip the following paragraph...

google maps has a good data set for about half the countries, available through the normal map tiles. The other half of the countries are basically blank patches with a few roads running through them. But in those countries you can load mapmaker tiles (which are basically the wiki version, edited by users but not graduated to the normal tiles yet) which have alot more detail. But this is the thing: if you load the map using normal tiles it looks great if you're looking at one of the first half of the countries. And if you move the map to one of the second half, you get no detail. And vice versa - if you set the map type to mapmaker and go to a country that doesn't support mapmaker tiles all you get is blank space. So the following function checks if the map is centered within a countries "bounds box" - basically a rectangle made from each countries maximum and minimum latitudes and longitudes (these are stored as xml), and sets the map type accordingly. It also gets the map type currently displayed ("gm" for mapmaker, "m" for normal) and stops the map type getting set to the type it is already on.

Anyway, I just hacked it together blindly and it seems to work ok (the bounds box logic is not perfect I know but I couldn't think of another way short of making polygons out of every country in the world's borders) on fast machines, not so much on slower ones. I have no idea how to test if the lag is due to reading the xml and checking the conditions or waiting for google to serve the map tiles.

So, a) thanks for reading this far and b) do you think this is the best way to do this? The page is here (http://www.xelawho.com/map/ip2.php) and the function in question is below. Thanks again for any thoughts


function setMaptype() {
type =map.getCurrentMapType().getUrlArg();
var count=0;
for (var j = 0; j < gcountries.length; j++) {
if (lat>gcountries[j].minlat&&lat<gcountries[j].maxlat&&long>gcountries[j].minlng&&long<gcountries[j].maxlng) {
count++
}
}
if (type=="m"&&count>1) {
map.setMapType(G_MAPMAKER_NORMAL_MAP);
map.addMapType(G_MAPMAKER_NORMAL_MAP);
map.removeMapType(G_NORMAL_MAP);
map.removeMapType(G_SATELLITE_MAP);
map.removeMapType(G_HYBRID_MAP);
map.removeMapType(G_PHYSICAL_MAP);
map.addMapType(G_SATELLITE_MAP);
map.addMapType(G_MAPMAKER_HYBRID_MAP);
map.addMapType(G_PHYSICAL_MAP);
document.getElementById("maptype").style.color="red";
document.getElementById("maptype").innerHTML = "Mapmaker map";
}
if (type=="gm"&&count==1) {
map.removeMapType(G_MAPMAKER_NORMAL_MAP);
map.setMapType(G_NORMAL_MAP);
map.addMapType(G_NORMAL_MAP);
map.addMapType(G_HYBRID_MAP);
map.removeMapType(G_MAPMAKER_HYBRID_MAP);
document.getElementById("maptype").style.color="blue";
document.getElementById("maptype").innerHTML = "Normal road map";
}

map.addControl(new google.maps.MenuMapTypeControl(), new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7,7)));
}

Old Pedant
09-02-2011, 09:11 AM
LOL! WOW! You really do want to pick things I know nothing about, don't you?

If I were *betting* I would be that the JS code takes so close to zero time to execute as it doesn't matter. I'd bet it's all in loading the tiles.

But other than benchmarking... No way to know.

xelawho
09-02-2011, 09:24 AM
hmmm... can't tell if that's sarcasm or if I have actually found something you don't know about.

regardless. if the delay's on google's end there's not much I can do about it. thanks.

Old Pedant
09-02-2011, 09:35 PM
Not sarcasm! I really would have no idea without actually trying it. But when I look at all the stuff Google maps can do nearly instantly under *normal* circumstances, it makes me think that it's likely a network delay.



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum