Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

WAD Incorrect _do_loop values

May
3,515
5
The value of _do_loop at the termination of the DO loop is often one more than the numer of times the loop was actually executed.

Program:
Code:
@echo off
do while 0 gt 1
  echo in while loop: %_do_loop
enddo
echo after while %_do_loop

set x=0
do forever
  set /a/q x+=1
  echo in forever loop x=%x dl=%_do_loop
  if %x GT 2 leave
enddo
echo after forever loop %_do_loop

do n = 1 to 3
  echo in counted loop n=%n dl=%_do_loop
enddo
echo after counted loop n=%n dl=%_do_loop

Report:
Code:
after while 1
in forever loop x=1 dl=1
in forever loop x=2 dl=2
in forever loop x=3 dl=3
after forever loop 3
in counted loop n=1 dl=1
in counted loop n=2 dl=2
in counted loop n=3 dl=3
after counted loop n=4 dl=4

Only the forever loop reports _do_loop correctly.
 
In my opinion, they all get it right. Think of it this way: _DO_LOOP starts at 0 and whenever the DO condition is tested, it is incremented.
 
WAD. Like every other programming language in the world, the counter is incremented, then checked against the limit value. If it is greater, it falls out of the loop.
That's a loop termination control variable, not a loop execution counter. You do not increment an execution counter before you determine whether or not the loop is to be executed. Test first, increment second. The current report does not tell me how many times the loop was actually executed, e.g., how many lines were read from a file, it tells me how many times it checked whether or not to execute the loop. Depending on how the termination condition is specified, this test count may exceed the number of times the loop was executed. The definition of %_do_loop in HELP -> DO is execution count, not test count.

The command
do 0 (echo x) %+ echo %_do_loop
does not execute the ECHO X command, yet it reports one (1) execution.
 
Steve, when should the execution count be incremented ... ?

... right after the test? Then it would be counted even if the very next statement caused a LEAVE or an ITERATE.

... right before the next test (at the end of the loop)? Then it wouldn't be counted if it were after a LEAVE or an ITERATE.

I can't imagine an implementation that would satisfy everyone all the time. I haven't used any of DO's automatic variables except for testing their function. If I want something counted, I'll count it myself, precisely if and when it should be counted.

That said, I agree with you (partially ... I think). _DO_LOOP should not be incremented when the body of the loop is not entered.
 
Steve, when should the execution count be incremented ... ?

... right after the test? Then it would be counted even if the very next statement caused a LEAVE or an ITERATE.
That's exactly correct. That ITERATE or LEAVE statement is part of the loop. In terms of constructs in C, it ought to be analogous to:
int _do_loop = 0 ;
while ( loop execution test ) { _do_loop++ , loop body }


Notice I do not compare it with the for statement, nor with the FOR of BASIC or DO of FORTRAN. According to the documentation, _do_loop is a pure counter that is independent of repetition control. IIRC that was what was requested for V15. Unfortunately, the FEEDBACK mechanism does not provide access to suggestion history, nor to local copy, so I cannot verify.
 
Last edited:
That has some merit. But what about this (and variations)?
Code:
while ( condition ) { body; _do_loop++; }
There's not a one-size-fits-all.
 
Vince:
Your example counts not how often the loop body is executed, but how often the loop body is executed without executing LEAVE - in other words, it depends on the content of the loop body; my way is not.
 
My point is that none of the alternatives we have discussed may actually count what the user wants counted. That said, I prefer _do_loop **not** being incremented when nothing in the body of the loop is executed.
 
If I could redesign it, _do_loop would be strictly local to a given DO loop, not existing when a DO loop is finished, and reflecting the status of its immediately-enclosing DO loop. Otherwise, it's just nonsense when there are nested DO loops.
 
Last edited:
My point is that none of the alternatives we have discussed may actually count what the user wants counted. That said, I prefer _do_loop **not** being incremented when nothing in the body of the loop is executed.
How often is any particular program module executed? If it is one of those "single entry - single exit" modules greatly touted under the label "structured programming" in the 70s, it is as many times as it is entered. If a module has a single entry, regardless of how many exits it has the only logical way to count is the number of times it is entered. But under no conditions would I count performing a test before entering the module and never entering it as an execution of the module. Conceptually, the body of a DO loop is a module, and that is the count requested as a V15 enhancement.
If I could redesign it, _do_loop would be strictly local to a given DO loop, not existing when a DO loop is finished, and reflecting the status of its immediately-enclosing DO loop. Otherwise, it's just nonsense when there are nested DO loops.
Setting aside the issue of nest DO loops until later, the purpose of the loop counter surviving the ENDDO is to avoid the need to write code to count loop body executions, which could be the character count of a string, of a line or of a file, or the line count of a file, etc., values often required after the loop terminates.

Exiting nested DO loops
When LEAVE n was introduced in version 10, the highly useful internal variables reporting various command action counts were not yet invented. In retrospect, a slightly different syntax would have been more genneral. Many HLLs use named loops, allowing the equivalents of both ITERATE and LEAVE to be modified with the name of the outermost loop to be affected (and also allowing a single ENDDO with the specified loop name to be used instead of as many as are nested). The major advantage of named loops over the current LEAVE n syntax is that changing the nesting hierarchy by adding an additional level or removing a level would not always change the outermost level affected. This opens the question what syntax could we use to specify named DO loops? One possibility is a new option, /Z in my example: DO [/Z loopname] ...
For multilevel flow control dimply append the loop name after the ITERATE, LEAVE or ENDDO, e.g.,
ENDDO loopname is equivalent to the number of ENDDO statements required to exit from all loops nested inside the named loop and from the named loop.
How would the action counts of each level be reported? By a 2-dimensional array, one row for each named loop, one column for loop name, and one column for each of _do_loop, _do_errors, _do_dirs, and _do_files. The aray name could be _do_report...
 
When LEAVE n was introduced in version 10, the highly useful internal variables reporting various command action counts were not yet invented. In retrospect, a slightly different syntax would have been more genneral. Many HLLs use named loops, allowing the equivalents of both ITERATE and LEAVE to be modified with the name of the outermost loop to be affected (and also allowing a single ENDDO with the specified loop name to be used instead of as many as are nested). The major advantage of named loops over the current LEAVE n syntax is that changing the nesting hierarchy by adding an additional level or removing a level would not always change the outermost level affected. This opens the question what syntax could we use to specify named DO loops? One possibility is a new option, /Z in my example: DO [/Z loopname] ...
For multilevel flow control dimply append the loop name after the ITERATE, LEAVE or ENDDO, e.g.,
ENDDO loopname is equivalent to the number of ENDDO statements required to exit from all loops nested inside the named loop and from the named loop.
How would the action counts of each level be reported? By a 2-dimensional array, one row for each named loop, one column for loop name, and one column for each of _do_loop, _do_errors, _do_dirs, and _do_files. The aray name could be _do_report...

IMHO, that's way over the top. I wouldn't want to deal with it. And I wouldn't be surprised if Rex weren't inclined to go that route. TakeCmd.dll exports Do_Cmd(), suggesting that making all its "automatic" variables "local" (in the C sense) might be reasonable. **Environment** variables will survive after ENDDO; if I want more that that I'll do it myself.
 
IMHO, that's way over the top. I wouldn't want to deal with it. And I wouldn't be surprised if Rex weren't inclined to go that route. TakeCmd.dll exports Do_Cmd(), suggesting that making all its "automatic" variables "local" (in the C sense) might be reasonable. **Environment** variables will survive after ENDDO; if I want more that that I'll do it myself.
With absolutely no knowledge of the possible pitfalls, I can imagine how it might be done.
There's one _DO_LOOP, accessible by the user via a global pointer "INT *p_DO_LOOP". Each time a DO loop is entered, it does "INT *pprev_do_loop=p_DO_LOOP; INT my_do_loop; p_DO_LOOP = &my_do_loop;" ... and when exiting a DO loop ... "p_DO_LOOP=pprev_do_loop". I don't know how that would jive with "LEAVE N". If not so well, then maybe a "stack" of pointers to the various my_do_loop INTs might. Maybe it's supposed to do something like that now (but is buggy). It really doesn't make any sense to have one _DO_LOOP being incremented by some arbitrary number of nested loops.
 
IMHO, that's way over the top. I wouldn't want to deal with it. And I wouldn't be surprised if Rex weren't inclined to go that route. TakeCmd.dll exports Do_Cmd(), suggesting that making all its "automatic" variables "local" (in the C sense) might be reasonable. **Environment** variables will survive after ENDDO; if I want more that that I'll do it myself.
Vince:
I never used LEAVE n, so I agree with you that it would be used far too rarely to make the development effort worth while. This was more an exercise of generalization of the _do_loop[n] construct mentioned earlier.
 
Vince:
I never used LEAVE n, so I agree with you that it would be used far too rarely to make the development effort worth while. This was more an exercise of generalization of the _do_loop[n] construct mentioned earlier.
I don't think I've used LEAVE N, but it's a nice feature. _DO_LOOP[n] was a little tongue-in-cheek.
 
You seem to believe that the DO test is somehow outside of the loop. That is not the case.
Well, in the single-line DO the syntax does show it outside:
do 4 (echo Current loop body execution count: %_do_loop)

In the the C language all forms of iteration (while, for and do) explicitly separate the statement (or statement block) that is iterated from the iteration controls.That's the manner in which I always thought FOR and DO operate, and the above DO syntax reinforced my interpretation.

My original request for the _do_loop variable was not absolutely clear, but the intention was for a variable analogous to _do_files when execution control is not from a list of files, but that the two variables just mentioned be equal when found files control execution, i.e., in the code snippet below
do f in *.btm (REM)
echo %_do_loop
echo %_do_files
the two ECHOs would report the same values - as implemented the former is always 1 more than the latter...

I had not explicitly requested it, but I am glad the manner in which _do_loop is implemented it is available inside the loop. The two programs where I use it do just that, and do not examine it after the loop is exited, hence my delay of discovering the discrepancy from what I had (not as clearly as possible) asked.
 
From HELP -> DO:
"DO WHILE condition evaluates condition each time through the loop as a conditional expression before executing the loop, and will execute it only if it is true. If condition is FALSE when the DO is first executed, the loop will never be executed."

Let us consider the compound command
do while 1 lt 0 (echo In loop: %_do_loop) %+ echo After exit: %_do_loop
According to the documentation in HELP, the LOOP will not be executed at all. When executing the compound command above the only output is, in fact, from the second ECHO, just as the documentation above claims. BUT what _do_loop reports is not zero! It is 1 (one), conflicting with the documentation:
"%_do_loop The number of times the DO loop has been executed"

Well, in the single-line DO the syntax does show it outside:
do 4 (echo Current loop body execution count: %_do_loop)
No, it doesn't:
Code:
do 4 (echo Current loop body execution count: %_do_loop)
echo %_do_loop
Again, from HELP:
DO can be used in batch files, aliases, or at the command prompt. To use them in aliases or at the prompt, you need to define the DO on a single line, and enclose the body of the DO loop in a command group following the DO expression. (There is no ENDDO statement in a single-line DO). For example:
do count=1 to 10 by 1 (echo count=%count)

Clearly, the loop body is only what is enclosed in the command group, separated from the condition.
I do not see how displaying the value reported by _do_loop in a command executed AFTER the DO terminates changes the number of times the loop was executed.
 

Similar threads

Back
Top