Why are there no "else while" control statements?

Ff1a7adf06dc94b6271e4e632ce4aac0
0
0x4A45535345 101 Jan 29, 2010 at 05:07

Hey everybody.

I’ve always wondered why almost no languages have control statements such as “else while”, “else for”, “else do while”, “else for each”, “else switch”, and so on. It’s perfectly logical, and often one of these constructs is better suited to writing natural and logical code. It’s better than doing it by nesting control statements and restating the control conditions multiple times. Many control structures that are deeply nested (and excessively indented) can be simplified using these, making them easier to read and more flexible.

I know some C, C++, Java, and Python. I know that Python has the ability to attach an else after a while loop, but it apparently doesn’t allow the use of intermediate “else while” control statements (i.e. only the final else is allowed in it).

You can simulate an “else while” in C++ like so:

if(condition 1) { while(condition 1) {
   //actions to perform
}}
else if (condition 2) { do {  //else while
   //actions to perform
} while(condition 2); }
else{
   //actions to perform
}

Notice you have to write out the conditions twice, thereby forcing the programmer to maintain two identical condition statements when the code is modified.

Wouldn’t it be nice if you could just write:

while(condition 1) {
   //actions to perform
}
else while(condition 2) {
   //actions to perform
}
else {
   //actions to perform
}

The same thing goes for other control statements.

There’s no reason why they can’t implement this. Even if the underlying code has to check the conditions twice, the advantages of allowing these constructs would be great in my opinion. These control statements, although perhaps less common, are still often the most logical way to do certain things. I think part of the reason people don’t use them as much is precisely because they aren’t available, which hence discourages it, even though they make sense.

I find it surprising that even the more progressive and active languages haven’t implemented these constructs yet. It should have been standard to include them in all languages many years ago, in my opinion.

What do you think?

Do you know of any languages that actually implement most or all of these?

Do you know what the various language-creators’ reasons are for not including these control statements? It just doesn’t make sense to not put them in, to me.

Thanks for reading.

16 Replies

Please log in or register to post a reply.

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Jan 29, 2010 at 06:13

Well, I’ve never heard this particular idea proposed before…nor, off the top of my head, can I think of a time I’ve wanted this sort of construct. :) It may be that if I had an ‘else while’ I’d find ways to use it, or it may be that the usefulness of this statement is not really as high as you think it would be. :)

In all seriousness, though: when adding features to a language you reach a point of diminishing returns. The more stuff a language has in it, the more difficult it is to learn, and the greater the chance of unintended consequences. C++ has arguably already reached that point and gone far beyond it! So a feature like this that doesn’t seem like it would be useful that often, and would provide at best a marginal benefit in the use cases it does encounter, just doesn’t make the bar.

By the way, if I’ve understood your proposal correctly, you don’t actually need ‘else while’ to be a construct. You only need the ‘else’ clause on while loops; then ‘else while’ falls out naturally from C++ syntax rules, just like ‘else if’ does from chained ifs.

886c9946a5dd9887a33d237ddcc9b7e7
0
phresnel 101 Jan 29, 2010 at 09:07

I think those constructs are not natural (apart from their nearly non-existant real world use, as Reedbeta pointed out already).

If I read “if”, than the “else” does tell me what to do otherwise. Consider “while(X) else Y”; I read something like “as long as X, but otherwise Y”, or “do X until Y, otherwise Z”.

This is neither natural, nor logical to me. Than, a construct like

if (cond0) {
} else do {
} while (cond1) else {
}

somehow really chills me.

But maybe it is also the choice of keywords, something like

do {
} while (cond1) if_not_executed {
}

Dunno. But then again, the real world use has yet to find me.

Sidenote: This reminds me of a performance optimization:

if (cond) do {
    <crunch>
} while (cond);

(for the curious:

while(X) { <crunch> }
-->
start:
  if (!X) goto end;
  <crunch>
  goto start;
end:

or worse:

start:
  if (X) goto body;
  goto end;
body:
  <crunch>
  goto start;
end:

vs.

do { <crunch> } while(X)
-->
start:
  <crunch>
  if (X) goto start;

)

99f6aeec9715bb034bba93ba2a7eb360
0
Nick 102 Jan 29, 2010 at 10:51

@phresnel

Sidenote: This reminds me of a performance optimization:

Never complicate your code in an attempt to outsmart the compiler. It’s 2010, and any compiler will canonicalize your loop constructs and generate optimal code out of it.

Also, the cost of a mispredicted branch is quite high, so it pays off to cooperate with the branch predition logic. Sometimes this means longer code will actually execute faster than shorter code. So trust your compiler.

Only when you’re programming at the assembly level you might want to care about the performance of various loop constructs. When in C++, code C++, else there’s no point using a high level language.

3c5be51fdeec526e1f232d6b68cc0954
0
Sol_HSA 119 Jan 29, 2010 at 13:24

I’d love the original poster to post a case where this is useful.. of course it’s possible that in some programming styles you might end up writing things that way. Still, an example might illuminate things somewhat.

If it’s just a case of convenience, you might do things like..

#define ifwhile(x) if((x)) while((x))

=)

The compiler will optimize the duplicate comparisons out anyway. Or it had better, as that seems relatively simple thing to do.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Jan 29, 2010 at 13:51

@0x4A45535345

Hey everybody. I’ve always wondered why almost no languages have control statements such as “else while”, “else for”, “else do while”, “else for each”, “else switch”, and so on.

Actually, if they had, they would be call while else and for else. You want the loop construct to have an else part, you don’t want an else part to have a loop construct (which is already possible). A do else wouldn’t make sense, because the condition is only evaluated after the first iteration, and for a switch else you have the default case label.
@Sol_HSA

If it’s just a case of convenience, you might do things like..

#define ifwhile(x) if((x)) while((x))

Very dangerous.

char c;
ifwhile(c = std::cin.get())  // oops, you skip the first character
{
}
else
{
}
886c9946a5dd9887a33d237ddcc9b7e7
0
phresnel 101 Jan 29, 2010 at 14:42

@Nick

Never complicate your code in an attempt to outsmart the compiler. It’s 2010, and any compiler will canonicalize your loop constructs

My fail was to not attribute my post with “old+outdated”, so mea culpa.

and generate optimal code out of it.

“Optimal”, as in “perfect”?

Also, the cost of a mispredicted branch is quite high, so it pays off to cooperate with the branch predition logic. Sometimes this means longer code will actually execute faster than shorter code. So trust your compiler.

Not sure why you are trying to teach me that (“trust-your-compiler” vs. “longer-code-can-execute-faster” vs. “cooperate-w/-branch-prediction” vs. “trust-your-compiler”; that’ll confuse beginners), as …

#include <cstdlib>
#include <iostream>
extern bool foo (int &x);
extern void bar (int &x);
int main () {
        int b;
        std::cin >> b;
        srand (b);
#if 0
        b = rand();
        if (foo(b)) do {
                bar (b);
        } while (foo(b));
#else
        b = rand();
        while (foo(b)) {
                bar(b);
        }
#endif
}

b = rand();
    if (foo(b)) do {
            bar (b);
    } while (foo(b));       
-->
    call    _rand
    movl    %eax, 28(%esp)
    movl    %ebx, (%esp)
    call    __Z3fooRi
    testb   %al, %al
    je  L6          // assumed to not be taken
    .p2align 2,,3
L9:
    movl    %ebx, (%esp)
    call    __Z3barRi
    movl    %ebx, (%esp)
    call    __Z3fooRi
    testb   %al, %al
    jne L9
L6:

  

b = rand();
    while (foo(b)) {
            bar(b);
    }
-->
    call    _rand
    movl    %eax, 28(%esp)
    jmp L6
    .p2align 2,,3
L7:
    movl    %ebx, (%esp)
    call    __Z3barRi
L6:
    movl    %ebx, (%esp)
    call    __Z3fooRi
    testb   %al, %al
    jne L7

While different, those loops will eventually yield the same result.

Sidenote: MSVC08 will generate the exact same assembly for both variants with /O2:

; Line 15
    call    DWORD PTR __imp__rand
; Line 16
    lea ecx, DWORD PTR _b$[esp+8]
    push    ecx
    mov DWORD PTR _b$[esp+12], eax
    call    DWORD PTR ?foo@@3P6A_NAAH@ZA        ; foo
    add esp, 8
    test    al, al
    je  SHORT $LN8@main
$LL3@main:
; Line 17
    lea edx, DWORD PTR _b$[esp+4]
    push    edx
    call    DWORD PTR ?bar@@3P6AXAAH@ZA     ; bar
    lea eax, DWORD PTR _b$[esp+8]
    push    eax
    call    DWORD PTR ?foo@@3P6A_NAAH@ZA        ; foo
    add esp, 8
    test    al, al
    jne SHORT $LL3@main
$LN8@main:

Though I had to replace the extern declaration with function pointers, as I couldn’t get it give me the assembly for just one translation unit.

B7dcbc0c0f07253f25ff5c25fe38c081
0
SamuraiCrow 101 Jan 29, 2010 at 17:32
if(condition 1) { while(condition 1) {
   //actions to perform
}}
else if (condition 2) { do {  //else while
   //actions to perform
} while(condition 2); }
else{
   //actions to perform
}

could be written as:

if(condition 1){
   //actions to perform
   continue;
} else if (condition2) {
   //actions to perform
   continue;
} else {
   //actions to perform
}

Noting that there is never a reason to check the same conditional twice in a row, as was pointed out earlier, look up the continue command in some C reference and see if that is what you are looking for.

3c5be51fdeec526e1f232d6b68cc0954
0
Sol_HSA 119 Jan 30, 2010 at 05:31

@.oisyn

Very dangerous.

Well duh. Mind the smiley.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Jan 30, 2010 at 11:01

@Sol_HSA

Well duh. Mind the smiley.

Not everyone might have taken the hint from a sheer smiley :)

Ff1a7adf06dc94b6271e4e632ce4aac0
0
0x4A45535345 101 Jan 31, 2010 at 00:05

It’s certainly fair enough to say that the advantage may not be “great”, but nonetheless I think it could be nice to have. And, I’ll show you a reasonable example of it, after replying to some of the other statements.

In all seriousness, though: when adding features to a language you reach a point of diminishing returns. The more stuff a language has in it, the more difficult it is to learn, and the greater the chance of unintended consequences. C++ has arguably already reached that point and gone far beyond it! So a feature like this that doesn’t seem like it would be useful that often, and would provide at best a marginal benefit in the use cases it does encounter, just doesn’t make the bar.

It’s hardly even like “adding a new feature”, it’s more like completing the general control pattern of dependent control so that it applies to all relevant structures rather than just if.

By the way, if I’ve understood your proposal correctly, you don’t actually need ‘else while’ to be a construct. You only need the ‘else’ clause on while loops; then ‘else while’ falls out naturally from C++ syntax rules, just like ‘else if’ does from chained ifs.

By that logic, then you could also say that you “don’t really need else-if either” because it also follows from if and else. Thus, by your logic, you analogously argue that we should write dependent if statements as follows:

if(condition 1) {
   //actions
}
else {
   if(condition 2) {
      //actions
   }
   else {
      if(condition 3) {
         //actions
      }
      else {
         if(condition 4) {
            //actions
         }
         else {
            //actions
         }
      }
   }
}

:wacko:
But this is unwieldy, hence it is better to write it with else-ifs.

Writing the conditions in “else while” multiple times goes against the general programming principle that identical equivalent things should only have to be modified once (i.e. through 1 “variable”).

I think those constructs are not natural (apart from their nearly non-existant real world use, as Reedbeta pointed out already).

If I read “if”, than the “else” does tell me what to do otherwise. Consider “while(X) else Y”; I read something like “as long as X, but otherwise Y”, or “do X until Y, otherwise Z”.

This is neither natural, nor logical to me.

Regardless of how it reads to you, that’s not how a dependent control structure works. Else clauses create a dependent control structure, and the definition of a dependent control structure is that only one clause in it will execute. That’s the whole point of dependent control structures, and it’s why else clauses are different from unconditional code following a conditional.

Also, programs precede sequentially through code and don’t go backward unless you explicitly tell them to. Interpreting it as switching between the various conditions and actions, rather than exiting from whichever dependent control statement was selected, is contrary to the sequential nature of dependent control structures.

Sidenote: This reminds me of a performance optimization:

My use of a do while for the “else while” was indeed a performance optimization. It probably would have been clearer if I had wrote it in the slightly more wasteful, but more clean form. Apologies.

I’d love the original poster to post a case where this is useful.. of course it’s possible that in some programming styles you might end up writing things that way. Still, an example might illuminate things somewhat.

:happy:
I’d gladly give you an example. Here’s an example of using it in an internal list iterator. It may not be as efficient as an external iterator (do to redundancy), but it at least illustrates the use of the control construct. Consider it as pseudo-code.

//current_index declared somewhere in List class

Node List::operator[](int target_index) {
   distance = target_index - current_index;
   while(distance > 0) {
      go_to_next_node();
      --distance;
   }
   else while(distance < 0) {
      go_to_prev_node();
      ++distance;
   }
   return current_node;
}

Having to perform a particular distinct looping sequence of operations based on a condition (such as when on one side of a boundary or on the other side) is a very common phenomenon and can be more succinctly written with “else while” control. Sure, it’s so simple for binary conditions that one may not really care to have it available, but for more complex algorithms that involve more than two cases it could be a nice shorthand, especially if there’s additional levels of nesting involved in subsections of the algorithm.

Otherwise (without “else while”) you would write as follows:

//current_index declared somewhere in List class

Node List::operator[](int target_index) {
   distance = target_index - current_index;
   if(distance > 0) {
      while(distance > 0) {
         go_to_next_node();
         --distance;
      }
   }
   else if(distance < 0) {
      while(distance < 0) {
         go_to_prev_node();
         ++distance;
      }
   }
   return current_node;
}

Which, as I’ve said, forces you to write two identical conditions for each loop (which is bad).

Note however, that the function would in this case be more efficiently coded as follows (rather than either of the above two):

Node List::operator[](int target_index) {
   distance = target_index - current_index;
   if(distance > 0) {
      while(distance--) { go_to_next_node(); } 
   }
   else if(distance < 0) {
      while(distance++) { go_to_prev_node(); }
   }
   return current_node;
}

( Or, you could alternatively put “return current_node” statements after each while loop. Putting it at the end though, makes it so you only have to maintain one of such return statements (i.e. constant order), rather than one for each (i.e. linear order). )

Nonetheless, it doesn’t change the fact that the example that used the “else while” construct remedied the obnoxious issue of maintaining two separate copies of the condition. The fact that the more optimal form in this case had a while loop inside an if isn’t necessarily going to be the case for other uses of the “else while”. We were able to do that simply because of the dual use of the count integer as a condition check, which won’t always be possible for other constructs.

One other (less important) way it could of been programed is like this:

Node List::operator[](int target_index) {
   distance = target_index - current_index;
   while(distance != 0) {
      if(distance > 0) {
         go_to_next_node();
         --distance;
      }
      else if(distance < 0) {
         go_to_prev_node();
         ++distance;
      }
   }
   return current_node;
}

But, such a construct could make the program check things it already knows multiple times. Once we know the target index is on one side or the other, we don’t need to check the other side. So, this form is also inferior to the “else while” for this particular purpose.

As for macros, I generally try to avoid them as much as possible. The quirks and name collisions are unpleasant and harder to debug. Better to protect whoever ends up maintaining the code from them as much as possible.

A do else wouldn’t make sense, because the condition is only evaluated after the first iteration, and for a switch else you have the default case label.

That’s true I suppose. I hadn’t yet fully consider those ones, and had included them in the post for the sake of completion (i.e. so that the discussion would include them).

Apologies.

On a different subject though, it should be noted that I’m not talking about a single while loop that executes different things depending on conditions. What I’m talking about is a dependent control structure made up of while loops (and/or other things), which is different.

I am aware of the continue statement.

Is this what you meant, SamuraiCrow?:

while(condition) {
   if(condition 1) {
      //actions to perform
      continue;
   }
   else if(condition 2) {
      //actions to perform
      continue;
   }
   else {
      //actions to perform
   }
}

If so, you don’t need the continue statements anyway, unless there was more code in the while loop (or other loop type) that you needed to avoid executing. The dependent control structure would jump to the end of the while loop any time one of the clauses executed, and from there would continue to the top again on its own accord anyway.

Again, if this is what you meant, you should know it’s not what I’m talking about. It doesn’t even do the same thing as my example did. Strictly speaking, your example wouldn’t even compile. Continue statements are only valid in loops and your “equivalent” example wasn’t in one.

Perhaps you could explain what you meant, SamuraiCrow. I’m not sure if I understand your point.

My example only executes either the first while loop, the second while loop, or the else block. It never executes more than one particular block. It might execute one particular block multiple times, but it never executes multiple blocks in the dependent control structure. This is my intent.

Anyway, I guess I’ll leave it at that for now.

Thanks for reading and responding. Sorry for the length of my reply. Hopefully I wasn’t too wordy. :lol:

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Jan 31, 2010 at 00:25

@0x4A45535345

By that logic, then you could also say that you “don’t really need else-if either” because it also follows from if and else. Thus, by your logic, you analogously argue that we should write dependent if statements as follows:

Well, no. What I was saying is that that IS how the C compiler processes “else if” statements under the hood. Of course we don’t write them with all the extra indentation and braces like you showed, because C allows a block that consists of only a single statement to omit the braces. C doesn’t include “else if” statements as a built-in entity; they simply emerge when using another “if” as the single statement in an “else” block. I’m saying if you had a “while-else” statement in your language - that is, a while statement that could be followed by an optional else clause - you could then get what you’re calling “else while” in a similar fashion. I’m showing you how to get more for less, not shooting down part of your proposal. :)
@0x4A45535345

It’s hardly even like “adding a new feature”, it’s more like completing the general control pattern of dependent control so that it applies to all relevant structures rather than just if.

FWIW, I agree that a “while-else” statement makes some logical sense and can even be useful sometimes; Python includes it. It’s still more work for compiler designers to implement it and for users to learn the language. Moreover, the amount of benefit it provides is trivial in real terms. Even in your own example, you only eliminated three lines of code and one level of indentation. Sure, it may be more “logically complete”, but the marginal benefit still outweighs the marginal cost, IMHO.

3c5be51fdeec526e1f232d6b68cc0954
0
Sol_HSA 119 Jan 31, 2010 at 06:51

@0x4A45535345

//current_index declared somewhere in List class

Node List::operator[](int target_index) {
   distance = target_index - current_index;
   while(distance > 0) {
      go_to_next_node();
      --distance;
   }
   else while(distance < 0) {
      go_to_prev_node();
      ++distance;
   }
   return current_node;
}

If you just drop the ‘else’, it’ll work just fine =)

Anyway, while I see how it would “logically” fit the language structure (kind of like with try..catch..finally), I (still) don’t see it as too practical.

If you have a very complicated comparison operation which needs to be checked in several places, why not just store it in a temporary variable?

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Feb 01, 2010 at 13:24

@Reedbeta

Well, no. What I was saying is that that IS how the C compiler processes “else if” statements under the hood. Of course we don’t write them with all the extra indentation and braces like you showed, because C allows a block that consists of only a single statement to omit the braces. C doesn’t include “else if” statements as a built-in entity; they simply emerge when using another “if” as the single statement in an “else” block. I’m saying if you had a “while-else” statement in your language - that is, a while statement that could be followed by an optional else clause - you could then get what you’re calling “else while” in a similar fashion. I’m showing you how to get more for less, not shooting down part of your proposal. :D

Exactly. And by those same grammar rules, constructs like “else for” are already possible:

if (condition)
{
}
else for (int i = 0; i < num; i++))
{
}

This is completely legal C++ code, because the for(){} is just parsed as a single statement for the else-part. Furthermore, all these kinds of constructs (if, for, while, do..while) only accept a single statement! The accolades ({ and }) are just a means to convert a block of statements into a single statement, and that is why if() { statement1; statement2; } works.

B7dcbc0c0f07253f25ff5c25fe38c081
0
SamuraiCrow 101 Feb 01, 2010 at 21:18

@0x4A45535345

Is this what you meant, SamuraiCrow?:

while(condition) {
   if(condition 1) {
      //actions to perform
      continue;
   }
   else if(condition 2) {
      //actions to perform
      continue;
   }
   else {
      //actions to perform
   }
}

No that is not what I meant. If you thought that was what I meant, then you’d better check the code I was translating from. An if…continue is analogous to a while but it retains the else clause.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Feb 01, 2010 at 21:55

I don’t quite get whether you think that that is already possible, so let me just point out that it’s not and therefore needs to be added to the language, which will never happen because it breaks current code (the continue in the piece you quoted continues the while loop, so it breaks if it started to reïterate the if statement when compiling with a new compiler). HOWEVER, this will also be the case with 0x4A45535345’s proposal

if (a)
    while(:D { }
else { }

This currently attaches the else to the if. If 0x4A45535345’s feature was added, it ataches the else to the where, thereby changing current code.

Ff1a7adf06dc94b6271e4e632ce4aac0
0
0x4A45535345 101 Feb 02, 2010 at 07:14

…I’m showing you how to get more for less, not shooting down part of your proposal.

My mistake. Thanks for the info.

…but the marginal benefit still outweighs the marginal cost, IMHO

Fair enough. The surrounding conditions of loops can easily not be identical to the loop’s conditions, so that would further limit the “else while” in frequency of application. Also, it does have potential to be misread or misinterpreted, perhaps slightly more so than equivalent code that uses surrounding if and else if statements. Theoretically though, if we could just magically change history would we want it included in the language anyway (i.e. if it didn’t change the current code base)?

I guess my big issue with leaving it out would be the fact that control structures where the conditions are directly related and identical would force some form of redundant code (even with a temporary variable, you still have to write the condition’s name twice, which is somewhat logically redundant). C/C++ work well enough without it, but it could still be nice sometimes.

If you just drop the ‘else’, it’ll work just fine =)

Yeah, it sure will. It may even be slightly more efficient. However, there is a subtle difference.

Leaving the “else while” as just a while will cause it to become an independent control structure instead of a dependent one. Which means that both loops could theoretically execute, particularly if a programmer who’s maintaining it places code that changes the state of the condition variables between the two while loops. In contrast, when linked as a dependent structure (i.e. “else while”) it would clearly indicate to the maintainer that the state conditions should not change between the loops lest we risk executing both loops rather than one.

( Then again, I suppose one could argue that it also makes natural logical sense as independent control, depending on how you think about the algorithm. )

This is going to be an unrealistic example, but suppose we didn’t just have two while loops. Suppose we had 500 consecutive while loops, only one of which would ever execute upon condition evaluation.

If we used only the while loop it would look like this:

while(cond 1) {...}
while(cond 2) {...}
   ...
while(cond 499) {...}
while(cond 500) {...}

If we used “else while” it would look like this:

while(cond 1) {...}
else while(cond 2) {...}
   ...
else while(cond 499) {...}
else while(cond 500) {...}

Now, suppose that cond 1 is true. Therefore, for the example that uses only while loops the condition will first evaluate true, then the first loop will execute, then all 499 of the remaining loops will have their conditions checked. In contrast, for the example that uses “else while” the condition will first evaluate true, then the first loop will execute, then the computer will jump to the end of all of the remaining 499 loops, thereby performing only 1 condition check and 1 jump in total. Compare that with the 500 condition checks in the independent example.

Using independent control structures as dependent ones just because it happens to work for that particular source code at that particular time of writing is a bad habit in my experiences. It’s a habit that can lead to subtle and difficult bugs.

I think the question that should be asked is: Should only one of these blocks ever execute?

…why not just store it in a temporary variable?

Because, the temporary variable still has to have it’s name written to two separate condition evaluations and hence it only partially hides the redundancy. It doesn’t truly eliminate it, from the user’s perspective.

Still, I completely agree that a temporary variable is a good workaround and I would use one in that context. But, it would be nice if the language took care of the redundancy for me instead.

Exactly. And by those same grammar rules, constructs like “else for” are already possible: …

Cool, makes sense. Thanks for the info.

This currently attaches the else to the if. If 0x4A45535345’s feature was added, it ataches the else to the where, thereby changing current code.

Excellent point, well observed. Adding it to C/C++ would be fairly dangerous then I suppose.

The current C/C++ code base aside, if we could rewrite history would we want the construct included, or would we rather not have it?

Also, what about if it was in an arbitrary “idealized” language of our own design (i.e. if not restricted to C/C++ and its conventions)?

Anyway, thanks for the replies, as always.