How to Loop the Loop
HelpFile Links : Label | IfNotWindowOpen | Goto | Let | If | Goto | Repeat | Until | ReadFile | Separate | While | EndWhile | DayOfWeek | Hour
Related link : Avoiding Tight Loops
What's a Loop?
A loop is a sequence of instructions that is continually repeated until a specific condition is reached - or infinitely (until the user terminates the script).
Comic by Bill Amend. Original Source
In Macro Scheduler there are three looping constructs:
- Using a Label and a Goto statement
- Using Repeat and Until
- Using While and EndWhile
A loop may have conditional logic in it to decide whether the loop should continue or not.
Using Label and Goto
Here's a really simple never-ending (infinite) loop:
Label>start // do something here Goto>start
Hopefully that is self explanatory and doesn't need much saying about it. The Label statement marks a point in the script. The Goto statement causes execution to jump to the specified label. So in the above the script simply does the "// do something here bit" over and over and over again (until you stop the script or shut down the PC!).
Now, supposing you wanted to loop until a specific condition, say a specific window exists. You could do:
Label>start // do something here IfNotWindowOpen>Untitled - Notepad Goto>start Endif
So here all we're doing is only jumping back to start if the "Untitled - Notepad" window does NOT exist.
Or let's say you wanted to repeat the loop only 5 times. You could do:
Let>k=0 Label>start Let>k=k+1 // do something here If>k<5 Goto>start Endif
Here we have a loop counter "k" which we increment (add 1 to) in every iteration. At the end we check to see if k is less than 5 and if so we loop back. So the loop would stop when k reached 5. This is a rather clunky way of waiting 5 times and many real programmers hate goto statements, so a neater way is to use Repeat/Until which we'll look at next.
Using Repeat and Until
With Repeat/Until our "loop only 5 times" example becomes:
Let>k=0 Repeat>k Let>k=k+1 // do something Until>k=5
It's a bit neater, reads better and avoids the "spaghetti" code thing that overuse of Gotos could create. But at the end of the day the choice is yours.
Instead of a fixed number like 5 you might be looping through something dynamic like the lines in a text file or rows in Excel, and your Until condition would be checking for the loop counter becoming equal to the number of lines/rows:
ReadFile>c:\temp\test.txt,fileData Separate>fileData,CRLF,lines Let>k=0 Repeat>k Let>k=k+1 Let>this_line=lines_%k% //do something Until>k=lines_count
Using While and EndWhile
With While/EndWhile we could replicate the "loop 5 times" loop:
Let>k=0 While>k<5 Let>k=k+1 // do something EndWhile
The most noticeable difference is that we check the condition at the top of the loop. That's why in the example above the condition is k<5. Try stepping through the code with the debugger to see why that is.
Here's how we might implement our "Check for Untitled - Notepad" loop with a While/EndWhile:
Let>NotepadOpen=FALSE While>NotepadOpen=FALSE IfWindowOpen>Untitled - Notepad Let>NotepadOpen=TRUE Else // do something Endif EndWhile
While can take a "complex expression" as well. That's one with a combination of conditions, e.g. let's say we want to loop only if it is Monday morning:
DayOfWeek>day Hour>hh While>{ (%day% = 2) AND (%hh% < 12) } //do something DayOfWeek>day Hour>hh EndWhile
Confused? The DayOfWeek function returns the day number where 1 is a Sunday. So Monday is 2. If it's in the morning the hour must be less than 12. So our Loop condition must be "day is 2 AND hour is less than 12".
Breaking out of Loops Prematurely
Best practice is NOT to break out of a loop but you could jump out of one simply by using a Goto and Label. You might set a label after your loop and have an If/Endif condition inside the loop which uses a Goto to jump to the label if your exit condition turns true.
Remember, many hardcore geeks go a bit off colour if they see too many Gotos and if you're going to use an If/Endif anyway why not just set the loop condition to true and nest the code within your loop instead? We did exactly that in our While/EndWhile "Check for Untitled - Notepad" loop above.
Avoid Tight Loops
To keep the examples clear I haven't done so here but it's always worth adding a delay inside your loops. Here's an explanation as to why.