Tuesday, July 31, 2007

Control Loops

Part of my work is programming; as such, I occasionally run into a limitation of a language. Today, the language is C/C++ and the limitation is control loops.

Here's my beef:
If I want to make a loop where I do a complicated test every time I go around the loop, there are two options open to me:

First, I can use a while loop, and duplicate code.

foo()
{
a = xyzzy();
b = zxc();
c = bop();
while (a-b>c){
gazonk(a, b, c);
a = xyzzy();
b = zxc();
c = bop();
}
}
This is ugly. It demonstrates the problem, however. If xyzzy(), zxc(), and bop() are functions that are not fast to execute, saving them to a variable is imperative to performance. They can't simply be combined into a single true/false variable because gazonk needs them separately.

The second option is that I can embed my other calls in a function with references, and use a for loop.
foo()
{
for (bar(a, b, c); a-b>c; bar(a, b, c)){
gazonk(a, b, c);
}
}
bar(&a, &b, &c)
{
a = xyzzy();
b = zxc();
c = bop();
}


This one's not much better. It requires an extra function call per loop, and still requires a duplicate function call. I'd rather have a structure where I can always perform the same "init code" before the test. An extended do-while loop, if you will. Here's an example:
foo()
{
do{
a = xyzzy();
b = zxc();
c = bop();
} while (a-b>c){
gazonk(a, b, c);
}
}

It could be used with a for loop as well; for loops could be extended into a do-for loop.
foo()
{
do{
a = xyzzy();
b = zxc();
c = bop();
} for (int i=0; a-b>c; i++) {
gazonk(a, b, c, i);
}
}

The most interesting thing about this is that there is no good reason that this structure shouldn't exist. It would implement perfectly well in assembly. The loop test would simply not be the loop entry point.
So where's my do-for loop? And do any other languages implement this very useful and frequently used structure

4 comments:

Anonymous said...

Try this:

(Forgive the formatting, your blog doesn't allow pre tags in the comments>

foo()
{
  while (a = xyzzy(), b = zxc(), c = bop(), a-b>c)
    gazonk(a, b, c);
}

Anonymous said...

Here's a more complete program that will show it off:

#include <iostream>

int
main()
{
  int i;
  int j;
  int k;

  std::cout << (i = 0, j = 1, k = 2) << std::endl;

  return 0;
}

Unknown said...

You know, I never knew you could do that. Thanks.

Meanwhile, I have another thought about how to do it, though it's considered bad style, I believe.

foo()
{
while (true){
a = xyzzy();
b = zxc();
c = bop();
if (a-b>c) break;
gazonk(a, b, c);
}
}

Anonymous said...

Assuming that foo has to return something anyway, and that your compiler supports tail recursion as an optimisation:

int
foo()
{
a = xyzzy();
b = zxc();
c = bop();
if (a-b>c) return 0;
gazonk(a, b, c);
return foo();
}