I really liked this way of breaking out of a loop, and immediately started to experiment with it
My first idea was to define a %BREAK% macro like this:
This works, and is very readable, with code looking like: if condition %BREAK%
But there's a catch: It should only be used on the last line of a code block.
Else, the following lines
do get executed before breaking out of the loop. This might be OK in somes cases, but this is risky, as this %BREAK% macro does not behave like the break instructions in high level languages, which immediately break out.
This made me think that the right thing to do is actually to define WHILE or REPEAT macros, and get rid of this BREAK macro altogether.
And, thanks to the new ideas I borrowed above, this is actually simple and efficient.
Eventually, I ended up defining two sets of macros:
- %WHILE(% condition %)DO% ( block of code )
- %REPEAT% ( block of code ) %UNTIL(% condition %)ENDREP%
The condition is any valid test that can be used in an if statement.
Note that as the condition is evaluated once per loop, it must be used with delayed expansion enabled, so that the variables get updated every time.
The parenthesis in the %WHILE(% / %)DO% and %UNTIL(% / %)ENDREP% macro names are there only to make the macro language look more like C. If this proves to be a problem, we can just remove them.
Here's a test that demonstrates these two sets of macros:
(Note that it's using !TIME:~0,8! to quickly get the current time without the fractional part of a second. This is correct on my system, which uses the ISO time format HH:MM:SS.xx. But this may need to be adapted to your system depending on your locale.)
Code: Select all
@echo off
setlocal EnableDelayedExpansion
set REP16X=for /l %%- in (0,1,15) do if not defined -
set REP1MX=%REP16X% %REP16X% %REP16X% %REP16X% %REP16X%
set WHILE(=set "-=" ^& %REP1MX% ( if
set )DO=(set "-=") else set "-=-") ^& if not defined -
set REPEAT=set "-=" ^& %REP1MX%
set UNTIL(=^& if
set )ENDREP=set "-=-"
:# Wait for the end of the current second
%WHILE(% "%TIME:~0,8%"=="!TIME:~0,8!" %)DO% rem
:# Initial test with a goto
set "N=0"
set "T0=%TIME:~0,8%"
:encore
if "%T0%"=="!TIME:~0,8!" (
set /a "N+=1"
goto :encore
)
echo goto loop looped !N! times in 1 second
:# Wait for the end of the current second
%WHILE(% "%TIME:~0,8%"=="!TIME:~0,8!" %)DO% rem
:# Test with a while loop
set "N=0"
set "T0=%TIME:~0,8%"
%WHILE(% "%T0%"=="!TIME:~0,8!" %)DO% (
set /a "N+=1"
)
echo while loop looped !N! times in 1 second
:# Wait for the end of the current second
%WHILE(% "%TIME:~0,8%"=="!TIME:~0,8!" %)DO% rem
:# Test with a repeat loop
set "N=0"
set "T0=%TIME:~0,8%"
%REPEAT% (
set /a "N+=1"
) %UNTIL(% not "%T0%"=="!TIME:~0,8!" %)ENDREP%
echo repeat loop looped !N! times in 1 second
endlocal & exit /b 0
The output of that code shows that the new while and repeat loops are considerably more efficient than loops using a goto:
Code: Select all
goto loop looped 93 times in 1 second
while loop looped 7098 times in 1 second
repeat loop looped 11700 times in 1 second
A bit disappointing though, the WHILE( macro is significantly slower than the REPEAT macro. We need to find a way to further simplify it to make it as fast as the other.
I've not tested them
inside other macros, but I think this should work.
The only limitation so far is that we can't nest multiple such loops inside one another, because they all share the same - exit variable.