digitalmars.D - [Language construct idea] The for loop with prepare step
- Quirin Schroll (24/24) Feb 12 Extend `for` to `for (init; prepare; condition; increment) body`.
- Paul Backus (4/7) Feb 12 I've never heard of anything like this in any other programming
- Quirin Schroll (18/28) Feb 13 I thought it was obvious, but okay:
- H. S. Teoh (71/96) Feb 12 The primary issue here is that the conventional looping constructs (the
- Richard (Rikki) Andrew Cattermole (13/28) Feb 12 Oh I quite like this.
- Quirin Schroll (68/86) Feb 13 Me, too. I love this:
- Quirin Schroll (4/12) Feb 15 I filed this as an enhancement:
- H. S. Teoh (20/27) Feb 13 The problemm with this syntax is that it looks too much like two
- Quirin Schroll (5/12) Feb 15 This looks like it is target of `continue`, which it (probably)
Extend `for` to `for (init; prepare; condition; increment) body`. The step `prepare` is executed before each check. Ignoring scope, the above is lowered to: ```d init; start: prepare; if (!condition) goto end; body; increment; goto start; end: ``` `continue` is also `goto start`. Sometimes, you want to execute something before each condition check, no matter if it’s when entering the loop or when looping back. D’s best options for that is: * Repeat yourself in `init` and `increment`, possibly using a local function if the step is more than 1 simple instruction. * Use `{ prepare; return condition; }()`. While not a DRY violation, this has the disadvantage that you cannot declare a variable in `prepare` that’s present in the loop’s `increment` or `body`.
Feb 12
On Monday, 12 February 2024 at 15:58:50 UTC, Quirin Schroll wrote:Extend `for` to `for (init; prepare; condition; increment) body`. The step `prepare` is executed before each check.I've never heard of anything like this in any other programming language, so I'm skeptical by default. Do you have any examples of code that this feature would improve?
Feb 12
On Monday, 12 February 2024 at 16:14:40 UTC, Paul Backus wrote:On Monday, 12 February 2024 at 15:58:50 UTC, Quirin Schroll wrote:I thought it was obvious, but okay: Instead of writing something like: ```d import core.stdc.stdio; for (int c; (c = fgetc(fp)) != EOF; ) { } ``` once could do: ```d for (import core.stdc.stdio; int c = fgetc(fp); c != EOF; ) { } ``` Example source: https://en.cppreference.com/w/c/io/fgetc (There, `c` is only used in the loop, and the declaration of `c` directly precedes the `while` loop only because in C89, `for` loops cannot use declarations.) It’s a standard example of a probably not that niche pattern. I’ve written this loop a lot of times learning and doing projects with C.Extend `for` to `for (init; prepare; condition; increment) body`. The step `prepare` is executed before each check.I've never heard of anything like this in any other programming language, so I'm skeptical by default. Do you have any examples of code that this feature would improve?
Feb 13
On Mon, Feb 12, 2024 at 03:58:50PM +0000, Quirin Schroll via Digitalmars-d wrote:Extend `for` to `for (init; prepare; condition; increment) body`. The step `prepare` is executed before each check. Ignoring scope, the above is lowered to: ```d init; start: prepare; if (!condition) goto end; body; increment; goto start; end: ``` `continue` is also `goto start`. Sometimes, you want to execute something before each condition check, no matter if it’s when entering the loop or when looping back. D’s best options for that is: * Repeat yourself in `init` and `increment`, possibly using a local function if the step is more than 1 simple instruction. * Use `{ prepare; return condition; }()`. While not a DRY violation, this has the disadvantage that you cannot declare a variable in `prepare` that’s present in the loop’s `increment` or `body`.The primary issue here is that the conventional looping constructs (the age-old for, while, do) enter the loop at the top, and exit the loop at the bottom. However, your general loop may exit the loop in the middle. (The other possibility -- entering the loop in the middle -- is equivalent to this case via a simple code rearrangement.) Therefore, you have this structure: loopStart: loopBodyBeforeCondition(); if (cond) break; // loop exit loopBodyAfterCondition(); goto loopStart; When loopBodyBeforeCondition is empty, you have a while-loop. When loopBodyAfterCondition is empty, you have a do-loop. When loopBodyAfterCondition only contains incrementing code, you have a for-loop (the initialization part of a for-loop technically belongs outside the loop, just before it begins). However, there is no direct equivalent to the case when neither loopBodyBeforeCondition nor loopBodyAfterCondition are non-empty (and the latter isn't just for incrementing). In such cases, most code resorts to hacks like `while(true)` with manual exit points. One example of the usefulness of a loop construct that supports the above construct in full generality is the outputing of list delimiters. If we postulate a syntax construct like: loop { loopBodyBeforeCondition(); } while(cond) { loopBodyAfterCondition(); } then, given some range r of items, we can output it with delimiters like this: loop { writef("%s", r.front); r.popFront; } while(!r.empty) { write(", "); } However, since we don't have such a construct, we have to resort to various hacks, like: while (!r.empty) { writef("%s", r.front); r.popFront; if (!r.empty) // <-- non-DRY write(", "); } Or: string delim; // hack: need an extra variable while (!r.empty) { writef("%s%s", r.front", delim); delim = ", "; // hack: lots of redundant assignments r.popFront; } Or: if (r.empty) return; writef("%s", r.front); // hoist first iteration out of loop body (non-DRY) r.popFront; while (!r.empty) { writef(", %s", r.front); // fold loopBodyAfterCondition into front of loop r.popFront; } The asymmetry of these hacks is caused by the underlying non-correspondence between the desired structure of the output (delimiter is placed between every pair of items) and the available structure of looping constructs in the language (none of the existing looping constructs exit in the middle of the loop body). Having a construct like proposed above solves the problem by making available a construct that has a 1-to-1 correspondence with the desired structure of the output. T -- Genius may have its limitations, but stupidity is not thus handicapped. -- Elbert Hubbard
Feb 12
On 13/02/2024 6:52 AM, H. S. Teoh wrote:One example of the usefulness of a loop construct that supports the above construct in full generality is the outputing of list delimiters. If we postulate a syntax construct like: |loop { loopBodyBeforeCondition(); } while(cond) { loopBodyAfterCondition(); } | then, given some range r of items, we can output it with delimiters like this: |loop { writef("%s", r.front); r.popFront; } while(!r.empty) { write(", "); } |Oh I quite like this. ```d do { } while(cond) { } ``` ```d foreach(...) { } while { } ``` I would use this a lot.
Feb 12
On Tuesday, 13 February 2024 at 00:59:36 UTC, Richard (Rikki) Andrew Cattermole wrote:On 13/02/2024 6:52 AM, H. S. Teoh wrote:Me, too. I love this: ```d do { … } while (cond) { … } ``` It’s a lot more structured than: ```d for (;;) { … if (!cond) break; … } ``` The only issue I see is it would probably be useful to have the variables declared in the top part be in scope in the lower part, however that is counterintuitive because of separated blocks. The less structured construct solves this. In my 4-statement `for` loop, it’s not surprising that a variable declared in the prepare step is present in the loop body. Alternatively, one can double-down on D’s for loop weirdness and allow this: ```d for ({int i = 1; int n = 10;} i < n; ++i) { } // already possible for (int i = 0; {int n = 10 - i; i < n;} ++i) { } // new! ``` Thinking about it, I actually like that more. The small semicolon could really easily be missed, while the braces are striking. I know I’ve seen this in some diagrams at university. Probably some form of Nassi–Shneiderman diagram. The University of Darmstadt has one here: https://www.iim.maschinenbau.tu-darmstadt.de/kursunterlagen_archiv/pst_ws1314/04-Methoden-3/Theorie/na sishneidermann.html (German) Image only:https://www.iim.maschinenbau.tu-darmstadt.de/kursunterlagen_archiv/pst_ws1314/04-Methoden-3/Theorie/endlosschleife.png On the other hand, this is rather obscure and I don’t intuit how it’ll work: ```d foreach(x; xs) { … } while { … } ``` This is my attempt to express it in terms of a `while` loop and range primitives: ```d for (;;) { … if (!xs.empty) break; auto x = xs.front; … xs.popFront; } ``` It’s weird to do stuff before you check a range for empty. It’s fine explicitly, but it would be weird to have `x` not be there in the first block after `foreach`. Correct me if I got the lowering wrong.One example of the usefulness of a loop construct that supports the above construct in full generality is the outputing of list delimiters. If we postulate a syntax construct like: |loop { loopBodyBeforeCondition(); } while(cond) { loopBodyAfterCondition(); } | then, given some range r of items, we can output it with delimiters like this: |loop { writef("%s", r.front); r.popFront; } while(!r.empty) { write(", "); } |Oh I quite like this.
Feb 13
On Tuesday, 13 February 2024 at 11:39:56 UTC, Quirin Schroll wrote:[…] Alternatively, one can double-down on D’s for loop weirdness and allow this: ```d for ({int i = 1; int n = 10;} i < n; ++i) { } // already possible for (int i = 0; {int n = 10 - i; i < n;} ++i) { } // new! ```I filed this as an enhancement: https://issues.dlang.org/show_bug.cgi?id=24395
Feb 15
On Tue, Feb 13, 2024 at 01:59:36PM +1300, Richard (Rikki) Andrew Cattermole via Digitalmars-d wrote: [...]```d foreach(...) { } while { } ```The problemm with this syntax is that it looks too much like two consecutive loops: ```d foreach(...) { } while (...) { } ``` Maybe a better syntax might be: ```d foreach(...) { } continue { } ``` So it becomes clear that the second block is part of the loop. T -- Chance favours the prepared mind. -- Louis Pasteur
Feb 13
On Tuesday, 13 February 2024 at 18:19:40 UTC, H. S. Teoh wrote:[…] Maybe a better syntax might be: ```d foreach(...) { } continue { } ```This looks like it is target of `continue`, which it (probably) wouldn’t be. I don’t think this feature makes no sense for `foreach` loops; those are high-level, and this is a low-level feature.
Feb 15