Useful alternative management structures?

Sometimes, when I program, I find that some specific control structure is very useful to me, but I do not have direct access in my programming language. I think my most common desire is something like a “split” (I have no idea what to actually call it):

{ foo(); } split_while( condition ) { bar(); } 

The semantics of this code will be that foo() always started, and then the condition is checked. If true, then bar() is executed, and we return to the first block (thereby running foo() again, etc.). Thanks to a comment by reddit zxqdms , I learned that Donald E. Knut writes about this structure in his article “Structured Programming with go to statements” (see page 279).

What alternative management structures, in your opinion, are a useful way to organize calculations?

My goal here is to give myself and others new ways of thinking about structuring code to improve fragmentation and reasoning.

Note. I do not ask how to generalize all possible control structures using macros jne , if / goto , Lisp, extensions, monads, combinators, quarks or something else, I ask what specializations are useful in describing the code.

+42
programming-languages loops control-structure
Nov 27 '10 at 20:27
source share
28 answers

What is fairly common is an endless loop. I would like to write this as follows:

 forever { // ... } 
+20
Nov 28 '10 at 2:12
source share

Sometimes I need a foreach loop with an index. This could be written like this:

 foreach (index i) (var item in list) { // ... } 

(I don't really like this syntax, but you understand)

+20
Nov 28 '10 at 3:14
source share

Loop with else:

 while (condition) { // ... } else { // the else runs if the loop didn't run } 
+18
Nov 28 2018-10-11T00:
source share

Most languages ​​have built-in functions to cover common cases, but fencepost loops are always difficult tasks: loops where you want to do something at each iteration, and also do something else between iterations. For example, combining strings with a separator:

 string result = ""; for (int i = 0; i < items.Count; i++) { result += items[i]; if (i < items.Count - 1) result += ", "; // This is gross. // What if I can't access items by index? // I have off-by-one errors *every* time I do this. } 

I know that folds may cover this case, but sometimes you need something imperative. It would be great if you could:

 string result = ""; foreach (var item in items) { result += item; } between { result += ", "; } 
+18
Nov 28 '10 at 8:59
source share
 { foo(); } split_while( condition ) { bar(); } 

You can do this quite easily using regular while :

 while (true) { foo(); if (!condition) break; bar(); } 

I do this quite often now that I have overcome my irrational aversion to break .

+14
Nov 27 '10 at 9:12
source share

If you look at Haskell, although there is a special syntax for various control structures, the control flow is often captured by types. The most common type of control types are Monads, Arrows, and applicative functors. Therefore, if you want to use a special type of control flow, this is usually some kind of higher-order function, and you can write it yourself or find it in the Haskells (Hackage) package database, which is quite large.

Such functions are usually found in the Control namespace, where you can find modules for parallel execution to handle errors. Many of the control structures commonly found in procedural languages ​​have an analog function in Control.Monad, among which are loops and if statements. If-else is an expression with keywords in haskell, if without else it makes no sense in the expression, but it makes perfect sense in the monad, so if statements without else are captured by the when and unless .

Another common case is a list operation in a more general context. Functional languages ​​like fold very much, while specialized languages ​​like map and filter . If you have a monad, then there is a natural fold extension. This is called foldM , and for this there are extensions to any specialized version of the fold you can think of, such as mapM and filterM .

+13
Nov 27 2018-10-23T00:
source share

With macros (lisp-style), tail calls, and continuations, this is all bizarre.

With macros, if standard control flow designs are insufficient for a given application, the programmer can write his own (and much more). To implement the constructs that you gave as an example, you need a simple macro.

Using tail calls, you can decompose complex control flow functions (for example, embedding a state machine) into functions.

Continuations are a powerful control flow primitive (try / catch is a limited version). In combination with tail calls and macros, complex flow control patterns (rollback, parsing, etc.) become straightforward. In addition, they are useful in web programming, since with them you can invert control inversion; you can have a function that asks the user for some input, does some processing, asks the user for more input, etc.

To rephrase the schema standard, instead of overlaying more functions in your language, you should try to remove the restrictions that make other functions necessary.

+9
Nov 27 2018-10-29T00-11-27
source share

This is just a general idea and syntax:

 if (cond) //do something else (cond) //do something also (cond) //do something else //do something end 

ALSO a condition is always evaluated. ELSE works as usual.

This also works for the case. This is probably a good way to eliminate the break statement:

 case (exp) also (const) //do something else (const) //do something also (const) //do something else //do something end 

can be read as:

 switch (exp) case (const) //do something case (const) //do something break case (const) //do something default //do something end 

I do not know if it is useful or just to read, but this is an example.

+9
Nov 28 '10 at 11:33
source share

if not:

 unless (condition) { // ... } 

not yet:

 until (condition) { // ... } 
+8
Nov 28 '10 at 3:31
source share

Marked outlines are what I sometimes lose from the main languages. eg.

 int i, j; for outer ( i = 0; i < M; ++i ) for ( j = 0; j < N; ++j ) if ( l1[ i ] == l2[ j ] ) break outer; 

Yes, I can usually mimic this with goto , but the equivalent for continue will require you to move the increment to the end of the loop body after the label, which impairs readability. You can also do this by setting a flag in the inner loop and checking it at each iteration of the outer loop, but it always looks awkward.

(Bonus: I sometimes wanted to have redo to go along with continue and break . It will return to the beginning of the loop without evaluating the increment.)

+8
Nov 28 '10 at 7:07
source share

I suggest the operator "then". It returns the left operand on the first iteration and the right operand on all other iterations:

 var result = ""; foreach (var item in items) { result += "" then ", "; result += item; } 

in the first iteration, it adds "to the result to all the others that it adds", ", so you get a line containing each element, separated by commas.

+8
Nov 28 '10 at 9:56
source share
 if (cond) //do something else (cond) //do something else (cond) //do something first //do something then //do something else (cond) //do something else //do something end 

FIRST and THEN are blocked if any of the three conditional expressions evaluates to true. FIRST runs before the conditional block, and THEN starts after the conditional block starts.

The ELSE conditional or final record following the FIRST and THEN statements are independent of these blocks.

It can read as:

 if (cond) first() //do something then() else (cond) first() //do something then() else (cond) first() //do something then() else (cond) //do something else //do something end function first() //do something return function then() //do something return 

These functions are just a form to read. They will not create frames. This is more like gosub / return from Basic.

Usefulness and readability as a matter of discussion.

+8
Nov 28 '10 at 12:25
source share

What about

 alternate { statement 1, statement 2, [statement 3,...] } 

for cyclic use of available operators on each subsequent pass.

Edit : trivial examples

 table_row_color = alternate(RED, GREEN, BLUE); player_color = alternate(color_list); // cycles through list items alternate( led_on(), led_off() ); 

Change 2 . In the third example above, the syntax can be a bit confusing as it looks like a function. In fact, on each pass, only one operator is evaluated, not both. The best syntax might be something like

 alternate { led_on(); } then { led_off(); } 

Or something like that. However, I like the idea that the result that is ever called can be used as desired (as in the color examples).

+6
Nov 28 '10 at 3:17
source share

D visibility guards are a useful control structure that is not observed very often.

+5
Nov 28 '10 at 9:43
source share

I think I should mention CityScript ( CityDesk scripting language ), which has some really fancy design loops.

In the help file:

 {$ forEach n var in (condition) sort-order $} ... text which appears for each item .... {$ between $} .. text which appears between each two items .... {$ odd $} .. text which appears for every other item, including the first .... {$ even $} .. text which appears for every other item, starting with the second .... {$ else $} .. text which appears if there are no items matching condition .... {$ before $} ..text which appears before the loop, only if there are items matching condition {$ after $} ..text which appears after the loop, only of there are items matching condition {$ next $} 
+5
Dec 03 '10 at 13:10
source share

Also note that many control structures get a new meaning in a monadic context, depending on the particular monad - look at mapM, filterM, whileM, sequence, etc. in Haskell.

+4
Nov 27 '10 at 21:59
source share

ignoring - ignore exceptions that occur in a specific block of code.

 try { foo() } catch { case ex: SomeException => /* ignore */ case ex: SomeOtherException => /* ignore */ } 

With the ignoring control ignoring you can write it more concisely and readably:

 ignoring(classOf[SomeException], classOf[SomeOtherException]) { foo() } 

[Scala provides this (and many other exception handling control constructs) in its standard library in the util.control package. ]

+4
Nov 28 2018-10-11T00:
source share

Something replacing

 bool found = false; for (int i = 0; i < N; i++) { if (hasProperty(A[i])) { found = true; DoSomething(A[i]); break; } } if (!found) { ... } 

as

 for (int i = 0; i < N; i++) { if (hasProperty(A[i])) { DoSomething(A[i]); break; } } ifnotinterrupted { ... } 

I always feel that there should be a better way than introducing a flag only to do something after the last (regular) execution of the loop body. You can check !(i < N) , but i is beyond the scope of the loop.

+4
Dec 03 2018-10-12T00:
source share

I would like to see a keyword for grouping output. Instead of this:

  int lastValue = 0; foreach (var val in dataSource) { if (lastValue != val.CustomerID) { WriteFooter(lastValue); WriteHeader(val); lastValue = val.CustomerID; } WriteRow(val); } if (lastValue != 0) { WriteFooter(lastValue); } 

what about the following:

  foreach(var val in dataSource) groupon(val.CustomerID) { startgroup { WriteHeader(val); } endgroup { WriteFooter(val) } } each { WriteRow(val); } 

If you have a decent platform, controls and / or report formatting, you will not need to write this code. But it is amazing how often I find myself in this. The most annoying part - the footer after the last iteration - is hard to do in a real life example without duplicating code.

+4
Dec 03 2018-10-12T00: 00Z
source share

This is a bit of a joke, but you can get behavior like you:

 #include <iostream> #include <cstdlib> int main (int argc, char *argv[]) { int N = std::strtol(argv[1], 0, 10); // Danger! int state = 0; switch (state%2) // Similar to Duff device. { do { case 1: std::cout << (2*state) << " B" << std::endl; case 0: std::cout << (2*state+1) << " A" << std::endl; ++state; } while (state <= N); default: break; } return 0; } 

ps formatting was a bit complicated, and I'm definitely not happy with that; however, emacs does even worse. Anyone want to try vim?

+3
Nov 28 2018-10-11T00:
source share

Python generators are really original if you mostly work with non-functional languages. More generally: sequels, shared routines, lazy lists.

+3
Nov 28 '10 at 4:28
source share

This is probably not considered, but in Python I was upset, there was no do loop.

Anto guarantees that I do not get any deviations from the answer to this answer, I get annoyed in any language I work for a period of time in which goto's is missing.

+3
Nov 28 2018-10-10T00:
source share
 for int i := 0 [down]to UpperBound() [step 2] 

Not available in every C-derived language.

Please think before you vote or write a comment:
This is not redundant for for (int i = 0; i <= UpperBound(); i++) , it has different semantics:

  • UpperBound() is evaluated only once.

  • The case UpperBound() == MAX_INT does not create an infinite loop

+3
Dec 03 2018-10-12T00:
source share
 foo(); while(condition) { bar(); foo(); } 
+2
Nov 28 2018-10-10T00:
source share

This is similar to @Paul Keister's answer .

(mumble, mumble) years ago, the application I was working on had many variations of the so-called interrupt handling - all this logic, which involves splitting the sorted rows of data into groups and subgroups with headers and footers, since the application was written in LISP, we captured common idioms in a macro called WITH-CONTROL-BREAKS. If I were to port this syntax to an increasingly popular squiggly form, it might look something like this:

 withControlBreaks (x, y, z : readSortedRecords()) { first (x) : { emitHeader(x); subcount = 0; } first (x, y) : { emitSubheader(x, y); zTotal = 0; } all (x, y, z) : { emitDetail(x, y, z); ztotal += z; } last (x, y) : { emitSubfooter(x, y, zTotal); ++subCount; } last (x) : { emitFooter(x, subcount); } } 

In this modern age, with common SQL, XQuery, LINQ, etc., this need does not seem to arise as much as before. But from time to time I want to have this management structure.

+2
Dec 12 2018-10-12T00:
source share

What about the PL / I style for loop ranges? Equivalent to VB:

 'Counts 1, 2, ... 49, 50, 23, 999, 998, ..., 991, 990
   For I = 1 to 50, 23, 999 to 990 Step -1

The most common use I see is to start a loop for a list of indexes, and then add another one. BTW, for each use it can also be convenient:

 'Bar1, Bar2, Bar3 are an IEnum (Wazoo);  Boz is a wazoo
   For Each Foo as Wazoo in Bar1, Bar2, Enumerable.One (Boz), Bar3

This will start a loop for all elements in Bar1, all elements in Bar2, Boz and Bar3. Linq would probably allow this without much difficulty, but internal language support might be a little more efficient.

+1
Dec 02 '10 at 18:59
source share

One of the management structures that is not available in many languages ​​is the I / O type structure. Like a switch type structure, it allows you to have a neatly formatted list of possible parameters, but matches the first, which is true (and not the first, which matches the input). A LISP of this type (which has it):

 (cond ((evenp a) a) ;if a is even return a ((> a 7) (/ a 2)) ;else if a is bigger than 7 return a/2 ((< a 5) (- a 1)) ;else if a is smaller than 5 return a-1 (t 17)) ;else return 17 

Or, for those who prefer a more C-shaped format

 cond (a % 2 == 0): a; break; (a > 7): a / 2; break; (a < 5): a - 1; break; default: 17; break; 

This is basically a more accurate representation of the if/elseif/elseif/else than the switch, and it can express this logic extremely efficiently in a clean, understandable way.

+1
Nov 14 2018-11-21T00:
source share

How about iterating with a moving window (from n elements instead of 1) through a list? This, in my opinion, is tangentially related to @munificent's answer .

Something like

 #python #sum of adjacent elements for x,y in pairs(list): print x + y def pairs(l): i=0 while i < len(l)-1: yield (l[i],l[i+1]) i+=1 

This is useful for certain types of things. Don't get me wrong, this is easy to implement as a function, but I think many people try to derive for and while loops when there are more specific / descriptive tools for the job.

0
Jun 06 2018-12-06T00:
source share



All Articles