3 ms·
Loops themselves are a design mistake. Look how wordy loop notation is. This is a sign of an internal struggle: the real thing does not fit a Procrustean concep
by Mikhail_Edoshin 2y ago
Loops themselves are a design mistake. Look how wordy loop notation is. This is a sign of an internal struggle: the real thing does not fit a Procrustean concept. In C we have five loop-related keywords: 'do', 'while', 'for', 'break', and 'continue'. All of them are syntactic sugar for 'goto'. Yet all that sugar is not sufficient for certain cases of control flow. And these cases are not even exotic. We all know how to loop forward in C:
for (i = 0; i < n; ++i)
Now let's try to loop backward:
for (i = n; i--; )
Why this thing is so asymmetric to the forward case? And doesn’t it, perhaps, start to become a little too idiomatic? Now, if we try to rewrite them with 'goto':
i = 0;
a: <body>
++i;
if (i < n)
goto a;
i = n;
b: --i;
<body>
if (i > 0)
goto b;
Isn’t this more symmetric? Can we now see that the chief reason of asymmetry is that 'n' is not a valid value for 'i' but '0' is?
'Goto' is, of course, an implementation detail and is not convenient for reasoning. Thing is loops are not necessary for reasoning either. They are an implementation detail mistakenly dressed into reasoning clothes.
- trealira 2y agoI also think that some algorithms can be expressed better using goto than with a combination of nested loops, break, and continue. For example, Donald Knuth talks about the "basic backtrack" algorithm in fascicle 5 [0], which he uses to implement a solution to the N-queens problem. It's possible to express it as a set of while loops (which someone on Stack Overflow showed [1]), but it's, IMO, less readable than the goto version. Of course, the recursive solution is the most readable anyway. Edit: Also, both the goto version and while loop version of that algorithm on the Stack Overflow post have an out of bounds index when col and row are both zero. That algorithm was meant for 1-indexed arrays and was not properly translated. [0]: https://cs.stanford.edu/%7Eknuth/fasc5b.ps.gz https://cs.stanford.edu/%7Eknuth/fasc5b.ps.gz [1]: https://stackoverflow.com/questions/78614303/can-knuths-algorithm-b-be-written-without-goto-statements-or-recursion https://stackoverflow.com/questions/78614303/can-knuths-algo...
- Mikhail_Edoshin 2y agoThis is absolutely true. Even advanced loop syntax implies nesting; but control flow in general is not hierarchical. Simple loop syntax in C is even more limited, because it uses a fixed place for the loop condition test: either at the start or at the end of the body. As a result if we need to do something, then test the condition, then do something extra, we are stuck. For example, we may want to print a list of stings separated with commas: void printManyStrsWithCommas(char *s[], int n /* trusted to be positive */) { int i = 0; a: printOneStr(s[i]); ++i; if (i < n) { printOneStr(", "); goto a; } } Here the loop condition is 'i < n', but when it is true, we need to do one more thing (print a comma) before resuming the loop. If we are to stick with non-'goto' syntax we have the following options: - Somehow cram it into the loop expression with the comma operator. This is very limited because it is an expression. For example, what if 'printOneString' can err? - Somehow use 'while(1)' with 'break'. It may even compile to the same code as with 'goto'. But why, then, we have to use an unbounded loop syntax with a clearly bounded sequence? - Somehow add additional variables and tests and either lose efficiency or trust the compiler to lead us out. With 'goto' the code does only what is necessary. What 'goto' does not do is that it does not immediately convey a clear idea that the code repeats certain steps.
- trealira 2y agoIn this particular case I think you could do one of the following (ignoring the "while (true)" + "break" combination): 1: while (printOneStr(s[i]), ++i < n) printOneStr(", "); 2: goto start; do { printOneStr(", "); start: printOneStr(s[i]); ++i; } while (i < n); But for this kind of code in general, I have thought that the "do while" statement should be extended to take an extra statement after the "while (cond)" part. It could still be an empty statement, with just a semicolon, like "do ... while (cond);", exactly like it's already used; but in cases like yours, you would be able to write this: do { printOneStr(s[i]); ++i; } while (i < n) { printOneStr(", "); } As a bonus, the current mandatory semicolon after do-while statements would stop being a special case for the grammar of C.
- nedt 2y agoYou are writing it differently with the goto. You can do the same with the for loop and it becomes symmetric again for (i = 0; i < n; ++i); for (i = n; i >= 0; --i); (Note: not sure if intended that backward should run more often and include i = n)
- trealira 2y agoTo be symmetric, the loops should read like this: for (i = 0; i < n; i++) for (i = n; --i >= 0; ) The second one could also be this: for (i = n - 1; i >= 0; i--) I say this because your backwards loop accesses the inclusive range [0, n] while your forwards range accesses [0, n - 1].
- Maxatar 2y agoYour post is a good example of how backwards iteration is so error prone and fragile. Your reverse iteration would result in an out of bounds access as well as unsigned integer overflow which results in an infinite loop.