Allison Randal wrote: > You know, I almost made a very similar reply. But I read through > Damian's message a second time and changed my mind. C<BETWEEN> makes > sense as a C<NEXT> minus C<LAST>. As a C<PRE> minus C<FIRST> it's less > appealing. At the very least it begs a different name than "BETWEEN" (a > name that implies it executes "between" each loop, not at the beginning > of some). > > Hmmm... would C<LAST> not have the same problem as C<BETWEEN>? It also > "can't decide whether to execute until it knows whether the loop is > going to iterate again".
I've been watching this "Loop controls" thread with much interest. It is clear that there is some convenience in the concept of specialized blocks that execute FIRST or LAST or NEXT and BETWEEN has a large appeal, considering the large number of loops where something must be done every time except the first or every time except the last, except that I have never done the statistics to figure out whether every time except the last or every time except the first is more frequent, neither have I figured out whether BETWEEN is more frequently needed at the beginning or end of the loop body, and these latter two issues seem to be what everyone is going around and around about, without stating them. I think much of the discussion has resulted because there are many more conceptual meanings that could be useful, than there are suggested keywords to fulfil the meanings. I'll try to list the many conceptual meanings possible as I continue. As specialized blocks, the order is implicit, which means that separate specialized blocks would be needed for the four cases of BETWEEN which are: BETWEEN A) execute before the loop body every time except the first BETWEEN B) execute before the loop body every time except the last BETWEEN C) execute after the loop body every time except the first BETWEEN D) execute after the loop body every time except the last There has also been discussion about the "best" order of placement of the specialized loop blocks within the block, vs. the implicit execution order, and how that could be confusing sometimes. One thing I've noticed omitted from the discussion is that there are also cases such where the FIRST and BETWEEN might want to share code, or even the FIRST block might want to know if it is the last iteration. There has been the comment made that detection of the last iteration is hard (except, of course, for loop blocks with finite sequences) during the last iteration, that it is only after the last iteration that it is known that that iteration was, indeed, the last. It seems extremely trivial, however, to detect whether a particular iteration is the first. Now it could be said that LAST isn't needed, that you could just code that logic after the loop, but it seems to be true that LAST is only executed if the loop ever performs at least one iteration (and an explicit last statement is executed) or the iteration condition becomes false. This is a useful concept that (1) cannot be easily detected by user logic but can always be easily detected by the language loop control logic. The LAST block would certainly be a way to detect that, but the two cases differ: an explicit last statement could cause execution of the LAST block in the scope and with the variable values of the last iteration, but when the iteration condition becomes false, the scope and variable values are gone (unless heroically preserved, which probably could happen, but may have other nasty side effects that would be hard to understand). Also, the discussion of "else" conditions on loops seems very similar to LAST, so maybe we need five different LAST blocks: LAST A) executes when an explicit last statement occurs, in the scope of the last iteration LAST B) executes when the iteration condition becomes false, if at least one iteration has occurred LAST C) executes when the iteration condition becomes false, if there have been no iterations LAST D) legal only with loop loop: executes during the last iteration of a loop, before the loop body LAST E) legal only with loop loop: executes during the last iteration of a loop, after the loop body NEXT seems pretty simple: it executes after the loop body of an iteration, just before the iteration condition is reevaluated. Even FIRST could have two useful meanings: FIRST A) executes during the first iteration of a loop, before the loop body FIRST B) executes during the first iteration of a loop, after the loop body So it seems pretty clear that no amount of discussion will ever allow a few keywords to fulfil all of the possible variant implementations that might be possible and even useful, in various circumstances. I see several possible courses of action, there are perhaps more. 1) Choose the "most useful" meaning out of the above collections of possibilities, and disregard the others, forcing people who want to code them to "do it the old way" with discrete logic. 2) Invent new keywords for each possibility, proliferating keywords, and likely resulting in similar keywords having slightly different meanings, which could be easily confused, especially by newcomers. 3) Take a different approach to some of the issues. I'll outline a different approach that addresses at least a large number of the above possibilities, starting from the simplest. NEXT: The next statement is simple, but often there is logic that wants to be executed when it is known that the loop body is otherwise finished. A NEXT block could contain that, and it is pretty clear that a NEXT block would be executed after the loop body for an iteration. FIRST: Many times I've encountered situations where the special things that need to be executed during the first iteration of the loop cannot be gathered together to be executed first, nor can they be gathered together to be executed last. These seem like the only possibilities for a specialized FIRST block to be executed, and they don't cover the territory. Really, the concept of the FIRST iteration needs to be distributed and available throughout the loop body, and even available during NEXT blocks and LAST blocks. Instead of a FIRST block, I would recommend that all loop scopes contain a value or property that can be easily tested to determine if this code is executing as part of the first iteration or not. I hesitate to suggest syntax, but at least for loops that are part of a named block named NAME, perhaps NAME.first would work, or whatever works. I'll use this syntax for this discussion, but I'm not really suggesting it. Note that LAST D and LAST E are similar concepts, need to be similarly distributed, and could be resolved with a similar solution: NAME.last or whatever, but as a testable flag. For those cases where the logic can be gathered together, the following implementations could be used: FIRST A: PRE { if NAME.first { ... }} FIRST B: NEXT { if NAME.first { ... }} or maybe POST { if NAME.first { ... }} BETWEEN: Here are some implementations for variations of BETWEEN: BETWEEN A: PRE { unless NAME.first { ... }} BETWEEN B: PRE { unless NAME.last { ... }} BETWEEN C: NEXT { unless NAME.first { ... }} BETWEEN D: NEXT { unless NAME.last { ... }} Perhaps that is good enough, and no special BETWEEN block is needed. LAST: I'd suggest handling LAST D and LAST E as described in the section on FIRST, with testable flags. However, here are alternate implementations for them in the cases where the logic can be gathered together: LAST D: PRE { if NAME.last { ... }} LAST E: NEXT { if NAME.last { ... }} or maybe POST { if NAME.last { .... }} LAST A seems like a good candidate for a special block, to be executed in the scope and with the variables of the last iteration. LAST B and LAST C don't seem to be easily able to have available to them the variables and scope of the final iteration, since it is complete before they are known to be callable. LAST B could invoke a specialized block named FINISH and LAST C could invoke a specialized block named INSTEAD. Or, an additional state flag such as NAME.last could be set, to indicate that a LAST block was entered by an explicit last statement or not, and the same LAST block could be used for all these cases. Discrimination between LAST B and LAST C in that case could be done by checking the iteration counter, which could be NAME.count, and would be useful in lots of circumstances anyway. -- Glenn ===== Remember, 84.3% of all statistics are made up on the spot.