"ElseIf" is a "select-case" -- for use when, due to limitations of the programming language, you can't do select-case. -- JeffGrigg
Some languages, like C, C++ and Pascal impose a rather restrictive limitation on select-case statements, that each "case" value must be a scalar constant. In C/C++, this means that the "case" values can't even be strings.
A commonly used idiom to get around this is to use an "else-if" syntax: Write the next "if" right after the "else" of the previous "if", and don't consider the "ifs" nested.
Example:
if (stricmp(pzStatement, "select") == 0) { // Handle SELECT statement. } else if (stricmp(pzStatement, "insert") == 0) { // Handle INSERT statement. } else if (stricmp(pzStatement, "update") == 0) { // Handle UPDATE statement. } else if (stricmp(pzStatement, "delete") == 0) { // Handle DELETE statement. } else { // Some other command. }Many coding standards "recognize" this construct and recommend formatting it as above instead of something closer to the way the syntax tree would look:
if (stricmp(pzStatement, "select") == 0) { // Handle SELECT statement. } else` if (stricmp(pzStatement, "insert") == 0) { // Handle INSERT statement. } else if (stricmp(pzStatement, "update") == 0) { // Handle UPDATE statement. else if (stricmp(pzStatement, "delete") == 0) { // Handle DELETE statement. } else { // Some other command. }Anything wrong with using a function to convert the string to an int and then using the number in a switch statement? I tend to prefer that to huge if-then-else blocks. -- BruceIde
Yes. Where are you going to get the integer from? You'll end up with something like this:
int choice; if (stricmp(pzStatement, "select") == 0) choice = 1; else if (stricmp(pzStatement, "insert") == 0) choice = 2; else if (stricmp(pzStatement, "update") == 0) choice = 3; else if (stricmp(pzStatement, "delete") == 0) choice = 4; else ... ; switch (choice) { case 1: ... break; case 2: ... break; case 3: ... break; case 4: ... break; ... default: ... break; }You can get the integer using basic, ordinary math.
int choice; choice = (stricmp(pzStatement, "select") == 0) | 2*(stricmp(pzStatement, "insert") == 0) | 3*(stricmp(pzStatement, "update") == 0) | 4*(stricmp(pzStatement, "delete") == 0); switch(choice) { case 1: ... break; case 2: ... break; case 3: ... break; case 4: ... break; default: ... break; }Some alternate approaches:
Use discrete methods and merge the if-then-else and the case statement, for example,
if (stricmp(pzStatement, "select") == 0) Select(); else if (stricmp(pzStatement, "insert") == 0) Insert(); else if (stricmp(pzStatement, "update") == 0) Update(); else if (stricmp(pzStatement, "delete") == 0) Delete(); else UnknownCommand(pzStatement);Push the string comparison into the methods, for example,
if(Select(pzStatement)) {} else if (Insert(pzStatement)) {} else if (Update(pzStatement)) {} else if (Delete(pzStatement)) {} else UnknownCommand(pzStatement) {}Or, using the binary ternary, my favorite GccIsm?, you can do:
Select(pzStatement) ?: Insert(pzStatement) ?: Update(pzStatement) ?: Delete(pzStatement) ?: UnknownCommand(pzStatement);This is completely idiomatic and shouldn't be used in shared code without warning... but I find it quite nice, actually. I read ?: as "or else"; so the above is "select the statement or else insert it or else update it...". -- AdamBerger
The PerlLanguage version reads even cleaner (hehehe):
Select($Statement) or Insert($Statement) or Update($Statement) or Delete($Statement) or UnknownCommand($Statement);Create a set of function classes, put them in a collection, and iterate through the collection.
for(aFunction = myFunctionArray.begin(); aFunction != myFunctionArray.end(); ++aFunction) { if(aFunction.Do(this, pzStatement)) break; }Java provides a hashCode() method which can be quite handy when one is switching on strings. Okay, that might constitute a CodeStench, but since Java specifies the hash function precisely, one can hard code hash values into a switch without risk of being burned by runtime implementation issues.
Actually there are many more complicated cases, some of them are below. LispLanguages provide a construction called cond to deal with this kind of stuff. Here is an example using the SchemeLanguage:
(define describe (lambda (x) (cond ((integer? x) (display "x is an integer") (newline)) ((string? x) (display "x is a string") (newline)) ((character? x) (display "x is a character") (newline)) ((and (procedure? x) (= (arity x) 0)) (display "x is a thunk") (newline)) ((and (procedure? x) (= (arity x) 1)) (display "x is a monad") (newline)) (else (display "x is something else") (newline)))))LISP's COND is completely general. Since it selects the first passing case, it is more like a series of if-then-else's than like a C switch. The order of test cases can effect a COND's result, unlike the order of test cases in a C switch. LISP's cond clauses are evaluated in sequence and C's switch clauses are (conceptually) evaluated in parallel.
Another way of stating that is C's switch statement requires there to be only one matching case for a given value. Since there is only one matching case, there is no benefit to evaluating them in a particular order. Since Lisp's COND allows for more than one match, the programmer must order the cases to resolve ambiguity.
DylanLanguage has both select, which works like a C/C++/Java select-case statement, and case, which is exactly like the Lisp COND. Of course, Dylan picked it up directly from CommonLisp, and many people consider Dylan a Lisp dialect.
VisualBasic doesn't need this idiom because of the VbFlexibleSelectCase.
COBOL also doesn't need it because its select-case is a combination of everything one could imagine for select-case statements.
Cobol's EVALUATE statements are among the most flexible SelectCase? syntaxes I've ever seen, comparable to VB's definitely. Even the "Select Case True" idiom translates, which is kind of a SelectCaseIsElseIf. But in this case, repeating the pzStatement, and especially the stricmp function call, seems to violate OnceAndOnlyOnce.
Yes, the best way is to use CollectionAndLoopVsSelectionIdiom with thunks as the payload. It's also more efficient in terms of byte size. True me, I spent hours each day looking for these kinds of things and then optimizing them for the Palm. -- SunirShah
The COBOL select-case is by far the most flexible I've ever seen. (Scary flexible, in my opinion.) -- JeffGrigg
Incidentally, PythonLanguage has no select-case statement, but it does have an "elif" reserved word that keeps the indentation from getting out of control.
The idiomatic Python approach is to use a dictionary:
def this_func(): print "this func" def that_func(): print "that func" def main(): func = "this" switch = {"this" : this_func "that" : that_func } switch[func]()See the SnuspLanguage page for a version of this in PerlLanguage as used in a SNUSP interpreter. With perl's anonymous sub feature, there is no need for the intermediate names like "thisfunc", "thatfunc", etc...
Actually, pascal case statements work on sets not single constants, so you can test for multiple values in a given line e.g.
case x of begin
1..10 : DoSomething; SomeSet : DoSomethingElse; 20, 22, 24 : OrDoEvenThis; SetB - SetA : DoSomethingAsWasInAButNotB;end;
One semantic difference between "if" blocks (and elif, else, etc.) and "case" blocks - at least conceptually - is that "if" statements are sequential (the conditions are tested in order), whereas case blocks are parallel (though in many languages, case statements are sequential as well).
The parallel nature of case causes some interesting questions:
case (x): of (foo()) then (do something); of (bar()) then (do_something_else); esacwhere the "of" clauses contain function calls, is not legal. If it were, would all the "of" clauses be evaluated in advance? In what order?
The sequential nature of if-statements is an artifact of non-functional programming languages. In functional languages, AbstractStateMachines, and hardware description languages, all if statements are to be treated as if their conditions were evaluated in parallel. Guarding some if-statements with others can always be rewritten by the compiler. E.g.,:
if cond1 then if cond2 then conseq1 else altern1 else if cond3 then conseq2 else altern2may safely be rewritten as:
if cond1 & cond2 then conseq1 if cond1 & ~cond2 then altern1 if ~cond1 & cond3 then conseq2 if ~cond1 & ~cond3 then altern2In fact, this is the normal to write conditions in ASMs and HDLs.