View unanswered posts | View active topics It is currently 15 Sep 2014 03:28



Post new topic Reply to topic  [ 33 posts ]  Go to page Previous  1, 2, 3  Next
infinite loop with break condition 
Author Message
Expert

Joined: 12 Feb 2011 21:02
Posts: 1211
Location: United States (east coast)
Post Re: infinite loop with break condition
aGerman wrote:
It seems the cmd is parsing the syntax of IF even if it is not executed yet.
Is there a way to avoid that "early parsing" by any kind of escape sequence?

The IF statement MUST be parsed prior to execution. The comparison operator is needed at parse time, so delayed expansion cannot be used. The same is true for the IF /I option, and also for FOR options.

I can think of 2 solutions.

1) Dynamically build the macro prior to each execution, substituting the actual operator in the definition.

2) Anticipate all possible operators with considerably more code.
Code:
...
  if /i !_c2! == lss (%\n%
    if not !%%K! lss !_c3! exit !%%L!%\n%
  ) else if /i !_c2! == leq (%\n%
    if not !%%K! leq !_c3! exit !%%L!%\n%
  ) else etc.
...


Dave Benham


29 Dec 2011 09:44
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1768
Location: Germany
Post Re: infinite loop with break condition
Thanks Dave. Now I understand that behaviour.
Code:
@echo off &setlocal DisableDelayedExpansion

%$initWhile% :test
call :macros

cmd /c "%~f0" test num lss 50 ret num 5 ret 6
echo Macro returned %errorlevel%
pause
goto :eof

:::::::::::::::::::::::::::::::::::::::::::::::::

:test
%$While%
  set /a num+=1
  set /a ret+=num
  echo !num! !ret!
%$Wend%

:::::::::::::::::::::::::::::::::::::::::::::::::

:macros
set LF=^


set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"&rem TWO EMPTY LINES ABOVE REQUIRED!

set $initWhile=for /l %%I in (1 1 2) do if %%I==2 (%\n%
  if defined _arg (%\n%
    for /f %%J in ("!_arg!") do endlocal^&call %%J %%*%\n%
  ) else exit%\n%
) else setlocal EnableDelayedExpansion ^&set _arg=

set $While=(%\n%
  setlocal EnableDelayedExpansion%\n%
  call set "_args=%%*"%\n%
  if "!_args!"=="" exit 1%\n%
  for %%J in (!_args:* ^^=!) do if not defined _c1 (set "_c1=%%J") else (%\n%
    if not defined _c2 (set "_c2=%%J") else (%\n%
      if not defined _c3 (set "_c3=%%J") else (%\n%
        if not defined _ret (set "_ret=%%J") else (%\n%
          if not defined _var (set "_var=%%J") else (set "!_var!=%%J"^&set "_var=")%\n%
  ))))%\n%
  for /l %%J in (0) do (%\n%
    for /f "tokens=1,2" %%K in ("!_c1! !_ret!") do (%\n%
      if /i !_c2!==equ if not !%%K! equ !_c3! exit !%%L!%\n%
      if /i !_c2!==leq if not !%%K! leq !_c3! exit !%%L!%\n%
      if /i !_c2!==lss if not !%%K! lss !_c3! exit !%%L!%\n%
      if /i !_c2!==geq if not !%%K! geq !_c3! exit !%%L!%\n%
      if /i !_c2!==gtr if not !%%K! gtr !_c3! exit !%%L!%\n%
      if /i !_c2!==neq if not !%%K! neq !_c3! exit !%%L!%\n%
    )%\n%

 
set $Wend=))

goto :eof

Regards
aGerman


29 Dec 2011 10:18
Profile
Expert

Joined: 06 Dec 2011 22:15
Posts: 722
Location: México City, México
Post Re: infinite loop with break condition
Thanks a lot Dave, it works now!
Code:
set while=for %%n in (1 2) do if %%n==2 (%\n%
      call :StrLen argv argvLen=%\n%
      set "body=!argv:*do=!"%\n%
      call :StrLen body bodyLen=%\n%
      set /A condLen=argvLen-bodyLen-2%\n%
      for %%a in (!condLen!) do set "cond=!argv:~0,%%a!"%\n%
      echo for /L %%%%w in (1,0,1^^) do if !cond! !body! else exit ^^^^!whileResult^^^^!^> whileBody.bat%\n%
      cmd /V:ON /Q /C whileBody%\n%
      endlocal ^& set whileResult=^!errorlevel^!%\n%
   ) else setlocal EnableDelayedExpansion ^& set argv=
EDIT: I added /V:ON switch to avoid an error described below.

WHILE macro is used this way:
Code:
%WHILE% condition DO ( commands )

The condition is exactly the same of IF command.
To include multiple commands, terminate each line with %\n% (macro style). EDIT: Parentheses are mandatory.
To get a variable value in the condition or while body, enclose its name between %!%; this method is easier to remember than multiple ^^^^... and looks balanced (and I like it!) :)
"whileResult" variable preserve its (numeric) value after the while ends.
Some examples:
Code:
:factorial N R=
setlocal
set num=1
set whileResult=1
%while% %!%num%!% leq %1 do ( set /A whileResult*=num, num+=1 )
endlocal & if "%2" neq "" (set %2=%whileResult%) else echo %whileResult%
exit /B

:lcm n1 n2 lcm=
setlocal
set /a "whileResult=%1, j=%2"
%while% %!%j%!% neq 0 do ( set /a k=j, j=whileResult%%%%j, whileResult=k )
set /a j=%1*%2/whileResult
endlocal & if "%3" neq "" (set %3=%j%) else echo %j%
exit /B


call :factorial 10 factor=
echo Factorial of 10: %factor%
echo/

call :lcm 3527 3784 var=
echo Least common multiple of 3527 and 3784: %var%
echo/

echo Calculation of e:
set /A digits=6, one=1
for /L %%i in (1,1,%digits%) do set one=!one!0
set /A num=0, fact=1, factXone=fact*one, whileResult=0
echo #-   #%!%    term   summation
%while% %!%factXone%!% gtr 0 do ( %\n%
   set /A term=one/fact, whileResult+=term %\n%
   echo %!%num%!%-   %!%fact%!%   %!%term%!%   %!%whileResult%!% %\n%
   set /A num+=1, fact*=num, factXone=fact*one %\n%
)
echo Number e = !whileResult:~0,-%DIGITS%!.!whileResult:~-%DIGITS%!
echo/

Results:
Code:
Factorial of 10: 3628800

Least common multiple of 3527 and 3784: 13346168

Calculation of e:
#-   #!    term   summation
0-   1   1000000   1000000
1-   1   1000000   2000000
2-   2   500000   2500000
3-   6   166666   2666666
4-   24   41666   2708332
5-   120   8333   2716665
6-   720   1388   2718053
7-   5040   198   2718251
8-   40320   24   2718275
9-   362880   2   2718277
Number e = 2.718277


Last edited by Aacini on 14 Jan 2012 11:40, edited 2 times in total.



29 Dec 2011 12:08
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1768
Location: Germany
Post Re: infinite loop with break condition
Hi Aacini.

I like your syntax more than mine but I dont get it run. Have a look at what I thought you did to create the output. What's wrong?

Code:
@echo off
setlocal DisableDelayedExpansion
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

set while=for %%n in (1 2) do if %%n==2 (%\n%
      call :StrLen argv argvLen=%\n%
      set "body=!argv:*do=!"%\n%
      call :StrLen body bodyLen=%\n%
      set /A condLen=argvLen-bodyLen-2%\n%
      for %%a in (!condLen!) do set "cond=!argv:~0,%%a!"%\n%
      echo for /L %%%%w in (1,0,1^^) do if !cond! !body! else exit ^^^^!whileResult^^^^!^> whileBody.bat%\n%
      cmd /Q /C whileBody%\n%
      endlocal ^& set whileResult=^!errorlevel^!%\n%
   ) else setlocal EnableDelayedExpansion ^& set argv=

set !=^^^^!


setlocal EnableDelayedExpansion

call :factorial 10 factor=
echo Factorial of 10: %factor%
echo/

call :lcm 3527 3784 var=
echo Least common multiple of 3527 and 3784: %var%
echo/

echo Calculation of e:
set /A digits=6, one=1
for /L %%i in (1,1,%digits%) do set one=!one!0
set /A num=0, fact=1, factXone=fact*one, whileResult=0
echo #-   #%!%    term   summation
%while% %!%factXone%!% gtr 0 do ( %\n%
   set /A term=one/fact, whileResult+=term %\n%
   echo %!%num%!%-   %!%fact%!%   %!%term%!%   %!%whileResult%!% %\n%
   set /A num+=1, fact*=num, factXone=fact*one %\n%
)
echo Number e = !whileResult:~0,-%DIGITS%!.!whileResult:~-%DIGITS%!
echo/
pause
goto :eof


:factorial N R=
setlocal
set num=1
set whileResult=1
%while% %!%num%!% leq %1 do ( set /A whileResult*=num, num+=1 )
endlocal & if "%2" neq "" (set %2=%whileResult%) else echo %whileResult%
exit /B

:lcm n1 n2 lcm=
setlocal
set /a "whileResult=%1, j=%2"
%while% %!%j%!% neq 0 do ( set /a k=j, j=whileResult%%%%j, whileResult=k )
set /a j=%1*%2/whileResult
endlocal & if "%3" neq "" (set %3=%j%) else echo %j%
exit /B

:StrLen string [result=[adjust]]
setlocal EnableDelayedExpansion
set str=%1
set str=!str:"= !
if "!str:~0,1!" == " " (
    set "str=0%~1"
) else (
    set "str=0!%1!"
)
set len=0
for /L %%A in (12,-1,0) do (
   set /A "len|=1<<%%A"
   for %%B in (!len!) do if "!str:~%%B,1!" == "" set /A "len&=~1<<%%A"
)
for %%v in (!len!) do endlocal&if not "%2" == "" (set /A "%2=%%v%3") else echo %%v
exit /B


Regards
aGerman


29 Dec 2011 15:57
Profile
Expert

Joined: 06 Dec 2011 22:15
Posts: 722
Location: México City, México
Post Re: infinite loop with break condition
It looks good to me, I don't understand what can be wrong... :|

You may include ECHO's at several points to try to discover what the problem is.


29 Dec 2011 17:47
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1768
Location: Germany
Post Re: infinite loop with break condition
Thanks.

Just to tell you what happen:
- The factorial algorithm results in an infinte loop.
- The lcm comes up with an endless Division By 0 error.
- The calculation of e displays
Code:
Calculation of e:
#-   #!    term   summation
Number e = .0

Regards
aGerman


29 Dec 2011 18:06
Profile
Expert

Joined: 06 Dec 2011 22:15
Posts: 722
Location: México City, México
Post Re: infinite loop with break condition
I am posting again my complete WHILE macro test program:
Code:
@echo off
setlocal DisableDelayedExpansion
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

set while=for %%n in (1 2) do if %%n==2 (%\n%
      call :StrLen argv argvLen=%\n%
      set "body=!argv:*do=!"%\n%
      call :StrLen body bodyLen=%\n%
      set /A condLen=argvLen-bodyLen-2%\n%
      for %%a in (!condLen!) do set "cond=!argv:~0,%%a!"%\n%
      echo for /L %%%%w in (1,0,1^^) do if !cond! !body! else exit ^^^^!whileResult^^^^!^> whileBody.bat%\n%
      cmd /Q /C whileBody%\n%
      endlocal ^& set whileResult=^!errorlevel^!%\n%
   ) else setlocal EnableDelayedExpansion ^& set argv=

set !=^^^^!

setlocal EnableDelayedExpansion

call :factorial 10 factor=
echo Factorial of 10: %factor%
echo/
echo/

call :lcm 3527 3784 var=
echo Least common multiple of 3527 and 3784: %var%
echo/
echo/

echo Calculation of e:
echo/
set /A digits=8, one=1
for /L %%i in (1,1,%digits%) do set one=!one!0
set /A num=0, fact=1, term=1, whileResult=0
echo #- #%!%   term=1/#%!%    summation
%while% %!%term%!% gtr 0 do ( %\n%
   set /A term=one/fact, whileResult+=term %\n%
   echo %!%num%!%- %!%fact%!%    %!%term%!%    %!%whileResult%!% %\n%
   set /A num+=1, fact*=num %\n%
)
echo/
echo Number e = !whileResult:~0,-%DIGITS%!.!whileResult:~-%DIGITS%!
echo/
echo/

goto :EOF


:factorial N R=
setlocal
set num=1
set whileResult=1
%while% %!%num%!% leq %1 do ( set /A whileResult*=num, num+=1 )
endlocal & if "%2" neq "" (set %2=%whileResult%) else echo %whileResult%
exit /B

:lcm n1 n2 lcm=
setlocal
set /a "whileResult=%1, j=%2"
%while% %!%j%!% neq 0 do ( set /a k=j, j=whileResult%%%%j, whileResult=k )
set /a j=%1*%2/whileResult
endlocal & if "%3" neq "" (set %3=%j%) else echo %j%
exit /B


:StrLen string [result=[adjust]]
setlocal EnableDelayedExpansion
set str=%1
set str=!str:"= !
if "!str:~0,1!" == " " (
    set "str=0%~1"
) else (
    set "str=0!%1!"
)
set len=0
for /L %%A in (12,-1,0) do (
   set /A "len|=1<<%%A"
   for %%B in (!len!) do if "!str:~%%B,1!" == "" set /A "len&=~1<<%%A"
)
for %%v in (!len!) do endlocal&if not "%2" == "" (set /A "%2=%%v%3") else echo %%v
exit /B

Results:
Code:
Factorial of 10: 3628800


Least common multiple of 3527 and 3784: 13346168


Calculation of e:

#- #!   term=1/#!    summation
0- 1    100000000    100000000
1- 1    100000000    200000000
2- 2    50000000    250000000
3- 6    16666666    266666666
4- 24    4166666    270833332
5- 120    833333    271666665
6- 720    138888    271805553
7- 5040    19841    271825394
8- 40320    2480    271827874
9- 362880    275    271828149
10- 3628800    27    271828176
11- 39916800    2    271828178
12- 479001600    0    271828178

Number e = 2.71828178

Please test it and report what happened... :?


29 Dec 2011 20:55
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1768
Location: Germany
Post Re: infinite loop with break condition
Still wont work. The same behaviour like before :(

Regards
aGerman


30 Dec 2011 08:21
Profile
Expert

Joined: 06 Dec 2011 22:15
Posts: 722
Location: México City, México
Post Re: infinite loop with break condition
I don't understand what is happening. :? Are you using Win XP? Perhaps someone may have another idea? :?


30 Dec 2011 15:37
Profile
Expert

Joined: 12 Feb 2011 21:02
Posts: 1211
Location: United States (east coast)
Post Re: infinite loop with break condition
I get the exact same results as aGerman on both Vista and XP.

Dave Benham


30 Dec 2011 16:36
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1768
Location: Germany
Post Re: infinite loop with break condition
I'm on Win7.

I figured out what we have to do:
Code:
      cmd /V:ON /Q /C whileBody%\n%

/V:ON ! But why :?: Why does it not inherit the delayed expansion?

Regards
aGerman


30 Dec 2011 17:02
Profile
Expert

Joined: 12 Feb 2011 21:02
Posts: 1211
Location: United States (east coast)
Post Re: infinite loop with break condition
aGerman wrote:
Code:
      cmd /V:ON /Q /C whileBody%\n%

/V:ON ! But why :?: Why does it not inherit the delayed expansion?


I've run into this before. About the only thing that is inherited upon instantiation of a new CMD are the environment variable values.

Delayed expansion state, Command extension state, Echo state - they all revert back to the default state based on the registry. This is also true for implicit instantiation of CMD that occurs with FOR /F %%A IN ('some command'), and also on both sides of a pipe. I learned the significance of this at Why does delayed expansion fail when inside a piped block of code?


Now that you've got Aacini's code working, I'll have to try to figure out how it works.

Dave Benham


30 Dec 2011 18:44
Profile
Expert

Joined: 06 Dec 2011 22:15
Posts: 722
Location: México City, México
Post Re: infinite loop with break condition
WHILE macro is simple to use because it resemble an original Batch command, like IF or FOR. However, it is slow when it is repeatedly executed because the complete while-body is created again each time it is executed. I slightly modified WHILE macro, and renamed it to WHILEBODY, so it just create the whileBody.bat file, so it can be directly executed several times via CMD /V:ON /Q /C in a faster way. This modification also allows to execute whileBody.bat with parameters.
Code:
@echo off
setlocal DisableDelayedExpansion
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

set whileBody=for %%n in (1 2) do if %%n==2 (%\n%
      call :StrLen argv argvLen=%\n%
      set "body=!argv:*do=!"%\n%
      call :StrLen body bodyLen=%\n%
      set /A condLen=argvLen-bodyLen-2%\n%
      for %%a in (!condLen!) do set "cond=!argv:~0,%%a!"%\n%
      echo for /L %%%%w in (1,0,1^^) do if !cond! !body! else exit ^^^^!whileResult^^^^!^> whileBody.bat%\n%
      endlocal%\n%
   ) else setlocal EnableDelayedExpansion ^& set argv=

set !=^^^^!

setlocal EnableDelayedExpansion

%whileBody% %!%j%!% neq 0 do ( set /a k=j, j=whileResult%%%%j, whileResult=k )

call :lcm 3527 3784 var=
echo Least common multiple of 3527 and 3784: %var%
goto :EOF


:lcm n1 n2 lcm=
setlocal
set /a "whileResult=%1, j=%2"
cmd /V:ON /Q /C whileBody
set /a j=%1*%2/%errorlevel%
endlocal & if "%3" neq "" (set %3=%j%) else echo %j%
exit /B

The whileBody.bat file may be renamed after was created, so WHILE or WHILEBODY macros may create a second whileBody.bat file. This way, a while loop may be nested inside another one.
Code:
%whileBody% %!%j%!% lss 10 do (%\n%
   set /A mul=i*j              %\n%
   set /P =%!%mul%!%^< NUL     %\n%
   set /A j+=1                 %\n%
)
if exist whileBodyRow.bat del whileBodyRow.bat
ren whileBody.bat whileBodyRow.bat

echo Multiplication Table
echo/
set i=1
%while% %!%i%!% lss 10 do (     %\n%
   set j=1                      %\n%
   cmd /V:ON /Q /C whileBodyRow %\n%
   echo/                        %\n%
   set /A i+=1                  %\n%
)

Results:
Code:
Multiplication Table

1     2     3     4     5     6     7     8     9
2     4     6     8     10     12     14     16     18
3     6     9     12     15     18     21     24     27
4     8     12     16     20     24     28     32     36
5     10     15     20     25     30     35     40     45
6     12     18     24     30     36     42     48     54
7     14     21     28     35     42     49     56     63
8     16     24     32     40     48     56     64     72
9     18     27     36     45     54     63     72     81


30 Dec 2011 19:34
Profile
Expert

Joined: 22 Jan 2010 18:01
Posts: 1768
Location: Germany
Post Re: infinite loop with break condition
dbenham wrote:
Delayed expansion state, Command extension state, Echo state - they all revert back to the default state based on the registry. This is also true for implicit instantiation of CMD that occurs with FOR /F %%A IN ('some command'), and also on both sides of a pipe. I learned the significance of this at Why does delayed expansion fail when inside a piped block of code?

Yeah, my tests confirmed that. Thanks for the linked thread, very helpful and interesting!

Regards
aGerman


30 Dec 2011 20:34
Profile

Joined: 01 Sep 2011 03:54
Posts: 147
Location: Perth, Western Australia
Post Re: infinite loop with break condition
The simplest way that I have found to do it is this:
Code:
set "n=0"
:loop
set /a n+=1
if /i _"%n%"==_"10" (goto :exitloop)
ping -n 10 1.1.1.1
goto :loop
:exitloop
exit /b


Why (and how!) do you use macros to do infinite loops? And <b>aGerman</b>'s original topic post, it is not an infinite loop, it breaks after a set loop number.

Regards,
Rileyh


01 Jan 2012 02:33
Profile
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 33 posts ]  Go to page Previous  1, 2, 3  Next


Who is online

Users browsing this forum: Baidu [Spider], Google [Bot], Yahoo [Bot] and 20 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Forum style by Vjacheslav Trushkin for Free Forums/DivisionCore.