Using "Switch" versus multiple "if's"...any preference to be given one over the other?

 

I've long used sequential/successive "if" assessment logic in my codes but have wondered what value programming with switch/case holds over that of an "if" block.

For example, here is a successive "if" analysis routine:

   if(CalculatedSymbolType==1) CalculatedLeverage=NormalizeDouble(MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(Symbol(),MODE_MARGINREQUIRED),2);
   if(CalculatedSymbolType==2) CalculatedLeverage=NormalizeDouble(MarketInfo(Symbol(),MODE_ASK)*MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(Symbol(),MODE_MARGINREQUIRED),2);
   if(CalculatedSymbolType==3) CalculatedLeverage=NormalizeDouble(2*MarketInfo(Symbol(),MODE_LOTSIZE)/((MarketInfo(CalculatedBasePairForCross,MODE_BID)+MarketInfo(CalculatedBasePairForCross,MODE_ASK))*MarketInfo(Symbol(),MODE_MARGINREQUIRED)),2);
   if(CalculatedSymbolType==4 || CalculatedSymbolType==5) CalculatedLeverage=NormalizeDouble(MarketInfo(Symbol(),MODE_LOTSIZE)*(MarketInfo(CalculatedBasePairForCross,MODE_BID)+MarketInfo(CalculatedBasePairForCross,MODE_ASK))/(2*MarketInfo(Symbol(),MODE_MARGINREQUIRED)),2);
   if(CalculatedSymbolType==6) Print("Error occurred while identifying SymbolType(), calculated SymbolType() = ",CalculatedSymbolType);
versus the same logic implemented in switch/case coding:
   switch(SymbolType()) // Determine the type of currency pair and then determine the leverage of the instrument
      {
      case 1   :  CalculatedLeverage=NormalizeDouble(MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(Symbol(),MODE_MARGINREQUIRED),2); break;
      case 2   :  CalculatedLeverage=NormalizeDouble(MarketInfo(Symbol(),MODE_ASK)*MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(Symbol(),MODE_MARGINREQUIRED),2); break;
      case 3   :  CalculatedLeverage=NormalizeDouble(2*MarketInfo(Symbol(),MODE_LOTSIZE)/((MarketInfo(CalculatedBasePairForCross,MODE_BID)+MarketInfo(CalculatedBasePairForCross,MODE_ASK))*MarketInfo(Symbol(),MODE_MARGINREQUIRED)),2); break;
      case 4   :  CalculatedLeverage=NormalizeDouble(MarketInfo(Symbol(),MODE_LOTSIZE)*(MarketInfo(CalculatedBasePairForCross,MODE_BID)+MarketInfo(CalculatedBasePairForCross,MODE_ASK))/(2*MarketInfo(Symbol(),MODE_MARGINREQUIRED)),2); break;
      case 5   :  CalculatedLeverage=NormalizeDouble(MarketInfo(Symbol(),MODE_LOTSIZE)*(MarketInfo(CalculatedBasePairForCross,MODE_BID)+MarketInfo(CalculatedBasePairForCross,MODE_ASK))/(2*MarketInfo(Symbol(),MODE_MARGINREQUIRED)),2); break;
      case 6   :  Print("Error occurred while identifying SymbolType(), calculated SymbolType() = ",CalculatedSymbolType); break;
      default  :  Print("Error encountered in the SWITCH routine for calculating Leverage on financial instrument ",Symbol()); // The expression did not generate a case value
      }
What are the benefits of implementing switch/case versus implementing successive if/else code? Is one style less expensive in terms of cpu cycles?
 
Firstly, you are not taking advantage of one of the benefits of the 'case/switch' in that u can implement 'OR' in the switch... I'll use your example to explain:
   switch(SymbolType()) // Determine the type of currency pair and then determine the leverage of the instrument
      {
      case 1   :  CalculatedLeverage=NormalizeDouble(MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(Symbol(),MODE_MARGINREQUIRED),2); break;
      case 2   :  CalculatedLeverage=NormalizeDouble(MarketInfo(Symbol(),MODE_ASK)*MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(Symbol(),MODE_MARGINREQUIRED),2); break;
      case 3   :  CalculatedLeverage=NormalizeDouble(2*MarketInfo(Symbol(),MODE_LOTSIZE)/((MarketInfo(CalculatedBasePairForCross,MODE_BID)+MarketInfo(CalculatedBasePairForCross,MODE_ASK))*MarketInfo(Symbol(),MODE_MARGINREQUIRED)),2); break;
      case 4   :  
      case 5   :  CalculatedLeverage=NormalizeDouble(MarketInfo(Symbol(),MODE_LOTSIZE)*(MarketInfo(CalculatedBasePairForCross,MODE_BID)+MarketInfo(CalculatedBasePairForCross,MODE_ASK))/(2*MarketInfo(Symbol(),MODE_MARGINREQUIRED)),2); break;
      case 6   :  Print("Error occurred while identifying SymbolType(), calculated SymbolType() = ",CalculatedSymbolType); break;
      default  :  Print("Error encountered in the SWITCH routine for calculating Leverage on financial instrument ",Symbol()); // The expression did not generate a case value
      }
Note that both case 4 and case 5 would run the same statement and break at the same point out of the switch. There is no reason to repeat the statement twice.
In more complex examples, where there are many OR's, the switch case would look much cleaner than the equivalent IF code.


Regarding the speed - in most cases the 'switch/case' is slightly faster. In other programming languages the difference is usually bigger...
I guess MQL4's implementation is not as efficient. We can check your specific example (attached file) and the result for running each version 1,000,000 times:
2010.04.28 08:56:15     SpeedTest GBPUSD,H1: SWITCH time: 2547mSec
2010.04.28 08:56:12     SpeedTest GBPUSD,H1: IF time: 2687mSec

// on a different chart:
2010.04.28 08:57:11	SpeedTest EURCHF,H1: SWITCH time: 3813mSec
2010.04.28 08:57:08	SpeedTest EURCHF,H1: IF time: 3906mSec
Not a big difference... But faster.
Files:
speedtest.mq4  4 kb
 

Gordon, you the man! What an awesome robust answer to my question. (complete with the code for doing speed-tests, I had never seen this before, did not know it was possible)

So definitely the preference falls to using Switch instead of If in many portions of my code, certainly no reason to avoid it. And thanks for bringing the "OR" option to my attention. Code clarity always helps me save time in debug so the more whitespace the better.

 
1005phillip:

Gordon, you the man!

Both of you guys 'rule' in MQL4 !

You and others like you that are a small minority here and are definitely an assert to this forum.

We're losing more and more competent people such as yourself that are the backbone of support here for us NuBs and average coders in MQL4 to, quite understandably, MQL5.

This is very significant as MQ definitely doesn't provide didily squat for support to end users.

 

Is it considered "bad form" or ill-advised to leave out the "default" case in a switch? For those situations in which I know the switch will always result in an explicitly defined case can I forego the "default" case and keep my code all the cleaner?

example with unncessary "default" case included:

   switch(CalculatedSymbolType) // Determine the tickvalue for the financial instrument based on the instrument's SymbolType (major, cross, etc)
      {
      case 1   :  Print("Calculated TICKVALUE = ",DoubleToStr(MarketInfo(Symbol(),MODE_POINT)*MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(Symbol(),MODE_BID),6)," (Tick value in the deposit currency - base)"); break;
      case 2   :  Print("Calculated TICKVALUE = ",DoubleToStr(MarketInfo(Symbol(),MODE_POINT)*MarketInfo(Symbol(),MODE_LOTSIZE),6)," (Tick value in the deposit currency - counter)"); break;
      case 3   :  Print("Calculated TICKVALUE = ",DoubleToStr(MarketInfo(Symbol(),MODE_POINT)*MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(CalculatedCounterPairForCross,MODE_BID),6)," (Tick value in the deposit currency - ",AccountCurrency()," is Base to Counter)"); break;
      case 4   :  Print("Calculated TICKVALUE = ",DoubleToStr(MarketInfo(Symbol(),MODE_POINT)*MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(CalculatedCounterPairForCross,MODE_BID),6)," (Tick value in the deposit currency - ",AccountCurrency()," is Base to Counter)"); break;
      case 5   :  Print("Calculated TICKVALUE = ",DoubleToStr(MarketInfo(CalculatedCounterPairForCross,MODE_BID)*MarketInfo(Symbol(),MODE_POINT)*MarketInfo(Symbol(),MODE_LOTSIZE),6)," (Tick value in the deposit currency - ",AccountCurrency()," is Counter to Counter)"); break;
      default  :  Print("Error encountered in the SWITCH routine for calculating tickvalue of financial instrument ",Symbol()); // The expression is not needed as CalculatedSymbolType is guaranteed to only ever be a value of 1 through 5
      }
same switch without the "default" case:
   switch(CalculatedSymbolType) // Determine the tickvalue for the financial instrument based on the instrument's SymbolType (major, cross, etc)
      {
      case 1   :  Print("Calculated TICKVALUE = ",DoubleToStr(MarketInfo(Symbol(),MODE_POINT)*MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(Symbol(),MODE_BID),6)," (Tick value in the deposit currency - base)"); break;
      case 2   :  Print("Calculated TICKVALUE = ",DoubleToStr(MarketInfo(Symbol(),MODE_POINT)*MarketInfo(Symbol(),MODE_LOTSIZE),6)," (Tick value in the deposit currency - counter)"); break;
      case 3   :  Print("Calculated TICKVALUE = ",DoubleToStr(MarketInfo(Symbol(),MODE_POINT)*MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(CalculatedCounterPairForCross,MODE_BID),6)," (Tick value in the deposit currency - ",AccountCurrency()," is Base to Counter)"); break;
      case 4   :  Print("Calculated TICKVALUE = ",DoubleToStr(MarketInfo(Symbol(),MODE_POINT)*MarketInfo(Symbol(),MODE_LOTSIZE)/MarketInfo(CalculatedCounterPairForCross,MODE_BID),6)," (Tick value in the deposit currency - ",AccountCurrency()," is Base to Counter)"); break;
      case 5   :  Print("Calculated TICKVALUE = ",DoubleToStr(MarketInfo(CalculatedCounterPairForCross,MODE_BID)*MarketInfo(Symbol(),MODE_POINT)*MarketInfo(Symbol(),MODE_LOTSIZE),6)," (Tick value in the deposit currency - ",AccountCurrency()," is Counter to Counter)"); break;
      }
Is this considered broken code or bad programming for any reason?
 
I think that's a matter of opinion/coding-style and also depends on the specific situation. Personally, I usually do add it, exactly as in your first example - with some simple debug message. Can't harm...
 

Thanks for quick response, so it is advisable from a debug standpoint. Yeah I'll keep it then. Since I am only just now getting into using switches I figured I'd ask now so I develop the right/preferred usage habit from time-zero. I like that, "can't harm", works for me.

 
The theoretical answer is to include the default as an Assertion. While you can be sure that all the "cases" will be valid now, it is the future unexpected oops as the code goes through future modifications that you want to cater for.

Some bits from wiki:
Assertions are also sometimes placed at points the execution is not supposed to reach. For example, assertions could be placed at the default clause of the switch statement in languages such as C, C++, and Java. Cases that are intentionally not handled by the programmer will raise an error and abort the program rather than silently continuing in an erroneous state.
 
I've tried replacing the '||' on gordon's IF Speed-test with another simple IF, Switch still proves faster. Switch is great, if it only can test multiple conditions. Also why haven't I seen nested Switch used ?
 
cameofx:
Also why haven't I seen nested Switch used ?
For clarity's sake... A nested switch is almost always avoidable.
 
gordon:
Regarding the speed - in most cases the 'switch/case' is slightly faster. In other programming languages the difference is usually bigger...
I guess MQL4's implementation is not as efficient. We can check your specific example (attached file) and the result for running each version 1,000,000 times:
Not a big difference... But faster.

I'm a little suspicious that your timing difference is due to variations in the speed of external calls such as MarketInfo(). I've tried a comparison of if versus switch without such external calls or activity, and I'm very unsure that I'm seeing a non-random difference between the two routes. If forced to make a declaration on the basis of my testing, I'd hazard a guess that switch is very slightly faster than your if statements, but is not as fast as the following:

if (x == 0) {

} else if (x == 1) {

} else if (x == 2) { 

} etc

But the differences I'm seeing are so tiny that I'd unquestionably prefer - subjective - readability over any notional speed gain.

EDIT: and, stating the obvious, any speed difference between if and switch is almost always going to be trivial compared to other available optimisations. For example, caching the values of MODE_LOTSIZE and MODE_MARGINREQUIRED.

Reason: