PDA

View Full Version : Loops



Pancakes
07-03-2007, 08:53 AM
How To Design And Use Loops - By Pancakes

Hello all, and welcome to my first tutorial for SCAR :) Be forewarned, I'm not that good at explaining things, but I'll do my best here.

Ah loops. They're the magic that make scripts work. Without them, most scripts wouldn't be here today. They're a very important part of coding in all programming languages. First off lets take a look at a sample piece of code:
program AutoTalker;
begin
Writeln('Hello everybody!');
end.

Now what does that do? All it does, is send a message to the debug window, saying 'Hello everybody!' (without the quotation marks). Not very useful at all.

But what if you wanted to make it say 'Hello everybody!' more than once?
program AutoTalker;
begin
Writeln('Hello everybody!');
Writeln('Hello everybody!');
Writeln('Hello everybody!');
Writeln('Hello everybody!');
Writeln('Hello everybody!');
end.

Now that looks a little bit... pointless, doesn't it? All you are saying is say 'Hello everybody!' over and over again. Of course, you may actually want to do this. I'll introduce you the the first loop I'm going to teach you...

The 'For' Loop!

This is how it is coded in Scar.


var i : Integer;
begin
for i := 1 to 5 do
begin
Writeln('Hello everybody!');
end;
end;


Lets take this one line at a time.

var i : Integer;
All we are doing here is declaring 'i' as a variable. 'i' is the most commonly used variable in coding. You can use anything in the place of 'i', but you have to remember to declare it before you start the loop.
for i := 1 to 5 do
This is going to be a learning curve for you. First of all, we tell SCAR that we are using a loop, the for loop. Next off, you tell SCAR which variable you will be using for the for loop, in our case it is 'i'. Then, you make 'i' equal 1 to 5, which basically means, it will perform our 'loop' for every number from 1 to 5. Please note that it includes the first and last numbers to, not just the numbers in between. So 1 to 5 will do our loop 5 times, whereas, say, 0 to 5, will do our loop 6 times, because it counts 0 also. Next we put a 'do' after we have told SCAR how many times we want our loop to run. We are basically saying, "For everytime this loop is run, do...".
begin
This is where our loop begins. Everything you put in between this and the 'end;' will run however many times we set our loop to run.
Writeln('Hello everybody!');
Here, we are telling SCAR to write 'Hello everybody!' to the debug window.
end;
This is the end of our loop, so anything we put AFTER this will not be executed 5 times.

Now if you run this script, you should see 'Hello everybody!' written 5 times in the debug window. Thats great!

There is one more feature of the 'for' loop. Remember we declared that variable 'i' as an Integer? Well, that is our loop counter. You maybe be saying 'WTF is a loop counter????', I'm about to explain it to you. A loop counter is pretty much the amount of times the loop has executed. So if it is up to the 3rd time the loop has run, and you made the for loop as stated up, 'i' would be equal to 3. As a rule of thumb, always make 'for' loops start at one, it's much easier to keep track of what the loop counter is actually doing :)

Now why would we want to see how many times our loop has run?
Well, take a gander at this piece of code:
program ForLoops;
var i : Integer;
begin
for i := 1 to 5 do
begin
Writeln('This is line number: ' + IntToStr(i));
end;
end.

Now don't go and get scared off, it's actually quite simple. All we are doing, is Writing what line number it is, each time. How does it change though? Well, remember before I was talking about loop counters, and how every time the loop executes it updates the loop counter? Well we can use them in our script! So when you run this code, this is what you will see in your debug window:


This is line number: 1
This is line number: 2
This is line number: 3
This is line number: 4
This is line number: 5


How cool is that? It tells us what line number it is!

Well that concludes this section of the tutorial... Hopefully I'll get around to writing the other loops up :)

-Pancakes.

Pancakes
07-03-2007, 08:54 AM
Sorry, it's been a while since I worte up the last tutorial, but I've finally got around to writing the next one up. So here it is:

The Repeat...Until Loop
This loop if probably my favorite, and also, it is probably the most commonly used loop in programming. Briefly put, the repeat...until loop repeats a certain set of instructions, until a certain condition is met.

Lets take a quick look at a piece of code:

repeat
Writeln('This is a repeat... until loop!');
Wait(1000);
until(false);


So what does this do? Lets take it one line at a time.
repeat
This tells SCAR that we are going to be using a loop. The repeat loop. Repeat is always on it's own line, and nothing should come before or after it on that line. Put it this way - it's greedy and wants it all to itself :rolleyes:
Writeln('This is a repeat... until loop!');
As you should probably know, Writeln writes a string to the debug box, the little box underneath the coding window. So this line just writes to the debug box.
Wait(1000);
Wait will make our script wait for a certain amount of time, in this case 1000ms. If you are using an indefinate loop (I will explain below) it is VERY important to use waits.
until(false);
What this line does is tell SCAR to keep repeating the code until... well until false :redface:. Until(false); tells SCAR to keep repeating the loop FOREVER. This is all very well, but it is quite useless, and most, if not all, programmers will frown upon you for using until(false); This is because it is an indefinate loop, meaning it never ends! There are of course exceptions to this rule.

Why is the Wait important? Well, imagine that we didn't have the wait in there. What would the script do? It would keep writing to the debug box, over and over, and over, FOREVER. How would you stop this? You can't! When there are no breaks in a loop, and it keeps repeating forever, your computer will use up every last bit of computing power just to keep that script running, and eventually, your computer will crash. Thats why it is important to use the wait.

What can this loop be used for though when we are out in the big programming world, scripting away? Lets take a look at this example - granted it's a bit silly, but it gets the point across.

repeat
EatSandwich;
until(WeAreFull);


Do not try to put this into SCAR! It will NOT compile :) Take another look at the script, and read it from top to bottom. What does it say? Repeat EatSandwich Until(WeAreFull); or in english, Keep eating the sandwich until we are full. Its that simple!

Notice how we don't have any waits in there? We don't need any! This is not an indefinate loop, it is a definate one (meaning that it will end eventually). It will keep eating the Sandwich, until we are full. If we keep eating we're bound to get full eventually, thus it is a definate loop.


The While...Do Loop!
The while...do loop is very similar to the repeat...until loop. Infact, it is almost exactly the same. Lets take a look at both of them together.

//Repeat until loop
repeat
EatSandwich;
until(WeAreFull);


//While do loop
while (Not(WeAreFull)) do
begin
EatSandwich;
end;


Can you see the differences in those? The first loop, the repeat until loop, Eats a Sandwich and then checks to see if we are full.
However, the second loop, the while...do loop, checks if we are not full (i.e Not(WeAreFull)) and if we are not full, then eats the sandwich.

Lets take this step by step. Say we have eaten a massive lunch, and we are already full. If we were to use the repeat... until loop, we would eat a sandwich, and then check if we were full or not. By this time we would be feeling sick from eating so much.
If we were to use the while...do loop though, we would first check if we were already full, and if we were, not eat anything. Thus we wouldn't be sick!

In summary, the repeat until loop checks if the condition is true after it has executed the loop, the while do checks before.

I hope that that did make some sense to you, and helped you out there. :D

Next, we go onto...

The Goto and Label Loop!
Be forewarned, some programmers will frown upon you for using the Goto and Label loop, because it is an 'unnessecary indulgence' and it not proper programming. Moreover, from the Delphi Basics website...

The Goto keyword forces a jump to a given label.

It should Never be used in modern code since it makes code very difficult to maintain.

It is mostly used to force a termination of heavily nested code, where the logic to safely exit would be tortuous.

Never jump into or out of try statements, or into a loop or conditional block.


Personally, I use it whereever I see necessary, and personally, I don't really care about those 'some programmers'. :D

The Goto Label Loop involves two things - A label, which is a 'tag' or 'anchor' that the code can go back to, and the actual 'GoTo' statement, which makes SCAR jump back to the label. Lets take a look at some code:


var i : Integer;
label MyFirstLabel;

begin
i := 0;
MyFirstLabel:
i := i + 1;
if (i < 10) then goto MyFirstLabel;

Writeln('i is equal to: ' + inttostr(i));

end;


Now don't get scared off by seeing that. Regardless of how it appears, it is actually quite simple if you take it in a logical way.
We'll take it step-by-step.

var i : Integer;
label MyFirstLabel;


What we are doing here, is declaring a variable and a label. The variable 'i', will be used as our loop counter. We also have to declare a label to use in our script. Usually we declare it near the variable declarations. So we declare 'MyFirstLabel' as a label underneath the variable. Note how the declaration of a variable and a label differ.

begin
All we are doing here is telling Scar that we are beginning some code.
i := 0;
What we are doing here, is setting the variable 'i' to 0. By default, all new Integer variables are already 0, but I like to do this just to make sure. This part is unnessessary (sp?) but preferred.
MyFirstLabel:
Notice how there is no indentation? This is how labels should be declared, with no indentation at all, regardless of the nesting. Well, that how I do it anyway :D . Also, notice the colon after MyFirstLabel? You must remember to put that there.
i := i + 1;
Now we increment our variable, 'i', by one. You can also use Inc(i) to increase your variable by one, but I prefer this way as it is easier to tell what the code is doing.
if (i < 10) then goto MyFirstLabel;
Now this is the interesting part. We check if our variable, 'i', is less than 10. If it is, we jump back to our label, MyFirstLabel. If you hadnt noticed, that will mean that we keep executing this code:
i := i + 1;
every time, which increments it by one, until 'i' is not lower than 10! It really is that simple!
Writeln('i is equal to: ' + inttostr(i));
What this does, is after we have kept repeating our loop, we will write what 'i' is equal to. Do you know what? It will be equal to 10! (or should be, I am on a library computer at the moment.)
end;
This tells SCAR that we are ending our code, very standard stuff. :rolleyes:

In summary, the goto label loop is indeed an 'unnescessary indulgence', but it can be used usefully and correctly in the right manner. You must remember to declare the label though!

The Calling Procedures From Within Themselves Loop
^^What a name :rolleyes: Anyway...

This type of loop is one that I don't use very often at all. Infact, I tend to stay away from it at all costs, because debugging while using this, especially within complicated procedures, can be quite difficult. Nonetheless, some people have requested I explain this, so on we go. ;)

By now you should know what a procedure is, and how to use one. For this, I am going to be using the Sandwich example again.

This is the main loop of our code:
begin
EatSandwichUntilFull;
end.


As you can see there, all our program does is EatSandwichUntilFull, and then stops.
Lets take a look at our procedure, EatSandwichUntilFull.

procedure EatSandwichUntilFull;
begin
EatSandwich;
if (Not(ImFull)) then EatSandwichUntilFull;
end;


Lets examine this code.

procedure EatSandwichUntilFull;
begin
Here we are just declaring our procedure, EatSandwichUntilFull, and then beginning the procedure.
EatSandwich;
Here we tell SCAR to call the procedure, EatSandwich. Pretty basic stuff.
if (Not(ImFull)) then EatSandwichUntilFull;
This is we the fun starts. First of all, we check if we are still hungry by using if (Not(ImFull));. If we are, we call the procedure we are running from inside itself. Wait, let me get that right? You can call a procedure from inside itself? You sure can! When we run our code, what will happen? Lets say we are REALLY hungry, and it takes us 10 sandwiches to get full.

So when we call EatSandwichUntilFull, the code inside it runs, checks if we are still hungry (which we are) and then it runs EatSandwichUntilFull again. Then what? We check if we are still hungry (which we are) and we call EatSandWichUntilFull again! If you are thinking 'this loop could go on forever' you are quite right. But remember, first of all we check if we are still hungry, AND THEN eat. Eventually we will get full right? So eventually, our loop will end. Lets take a quick look at some more code, this code is WRONG, and it is completely possible that it would crash your computer.

procedure EatSandwichUntilFull;
begin
EatSandwich;
EatSandwichUntilFull;
end;


Now this loop will never end. It eats a sandwich, and then calls our procedure EatSandwichUntilFull again, WITHOUT CHECKING if we are full. So if we ran this code, it would just keep eating sandwiches forever.

To summarise, calling procedures from within themselves can be quite messy, but can also be quite beneficial in the right circumstances. Use common sense when writing them, and when debugging them, make sure you understand exactly what is going on.

Using Breaks

What is a break? Imagine you were at working, working away, and your boss tells you to go take a break. What does that mean? It means you stop working.
Lets take this and put it into SCAR. When Scar is repeating a length of code (or a loop) and you use the command Break;, Scar will stop executing the code that is inside of the loop, and jump right out of it. Once it does this, it will continue on with the rest of the code below the loop.
Now, we already know about labels (well you would if you had read the whole of the tutorial). Lets take a look at a little bit of code:

var i : Integer;
label MyFirstLabel;

begin
repeat
i := i + 1;
Writeln('Our variable i is equal to ' + IntToStr(i));
if (i > 10) then Goto MyFirstLabel;
until(false);
MyFirstLabel:
Writeln('Our variable i is over 10');
end;


Now, by taking a look at this code, you should be able to work out what it menas. First of all, we declare our variable and label, then we begin our code. Inside our code we have an indefinate loop, which adds 1 to i every time, and writes what i is equal to in the debug box.
Then we have a line which checks if i is over 10. If it is, we jump out of the loop, and write 'Our i variable is over 10'.
What is wrong with this code? Well, remember that labels and GoTo cannot be used to jump in and out of loops. Seems we have a loop, this would give an error in Scar.
Heres some different code that suits the same purpose:

var i : Integer;
begin
repeat
i := i + 1;
Writeln('Our variable i is equal to ' + IntToStr(i));
if (i > 10) then Break;
until(false);
Writeln('Our variable i is over 10');
end;

Now apart from this code not being as long, it is essentially the same. Can you spot the differences? Firstly, we have not declared a label. Infact, we do not use a label anywhere inside this code.
Notice this line:
if (i > 10) then Break;
What do we do here? First of all, as in the last script also, we check if our variable i is over 10. If it is, then we do something a bit tricky. We Break;
See if you can remember. What does Break do? Break makes us jump out of the loop. After we jump out of the loop, we will write to the debug box that i is over 10, which is quite correct.
After this, because we have no more code, our script ends. It's really as simple as that!

To summarise breaks, they are an extremely important part of scripting. You should take time to experiment, and to see what works / what doesn't. They are very useful for jumping out of loops, and in countless circumstances you will need to know how to use them and more importantly, how to use them correctly.

That concludes this series of tutorials. If you can think of anything that I have missed out, feel free to PM me and I will get around to writing it in.

Thanks for listening everyone.

Your man Pancakes :spot:

stuckman
07-03-2007, 09:04 AM
nice!
this explains it clearly and simply for those new to scripting
good job!

BobboHobbo
07-09-2007, 01:37 AM
Ah very good, this is good for a autotalker which you used in the example.

Good tut.

+REP

Sp0rky
07-09-2007, 01:40 AM
Yeah, really nice well explained tutorial for beginners. Good job. +rep

fbswift
07-17-2007, 10:26 PM
im just getting into scripting and it made perfect sense, thumbsup!

Pancakes
07-30-2007, 11:58 PM
-Bump- New tutorial added.

Pancakes.

Wanted
07-31-2007, 12:08 AM
There's no while do loops or goto labels :(

Pancakes
07-31-2007, 12:50 AM
There's no while do loops or goto labels :(

Not yet ;)

~alex~
07-31-2007, 01:04 AM
Or for loops, or calling procedure from within itself loops.

LordGregGreg
07-31-2007, 01:09 AM
great tut! The wiki has some good stuff in it too about loops.

Pancakes
07-31-2007, 01:34 AM
Or for loops, or calling procedure from within itself loops.

The For loops are at the top?

And I do not like calling procedures from within themselves, it makes for hard debugging (h)

Cheers all.

LordGregGreg
07-31-2007, 01:36 AM
And I do not like calling procedures from within themselves, it makes for hard debugging (h)

Cheers all.
indeed, they are messy.

But there are many time when a recursive loop is the only possible way to acomplish something (or at least the most efficant way)

I used to hate recursive loops, but they are prettty cool once u get used to wokring wiht them.

Wanted
07-31-2007, 03:11 AM
indeed, they are messy.

But there are many time when a recursive loop is the only possible way to acomplish something (or at least the most efficant way)

I used to hate recursive loops, but they are prettty cool once u get used to wokring wiht them.

An example please?

LordGregGreg
07-31-2007, 03:45 AM
An example please?
A binary search tree (data structure)

or (more specific) the Iterator that goes through it.

Pancakes
08-01-2007, 07:56 AM
*updated*

Pancakes
08-02-2007, 05:29 AM
179 views and 15 replies...?

BTW, updated :p

[I meant updated AGAIN lordgreggreg, you bastard :p]

Janilabo
08-02-2007, 09:15 AM
Looking good Pancakes. Great job! :)

Skys Shadow
08-09-2007, 05:28 AM
Great job pancakes! This really helped, especially the Breaks!

rotflmfwao
08-16-2007, 05:26 PM
I guess its not really a loop, but could what about With..Do loops? I've got one, but Idk where to put the classtype XD

EDIT: Pancakes... You rule.

Lalaji
08-16-2007, 07:00 PM
Pretty nice...

//While do loop
while (Not(WeAreFull)) do
begin
EatSandwich;
end;

Here does "WeAreFull" need to be a function?
Because I have tried 'While...do' With some pricedure and it dosent work.

rotflmfwao
08-16-2007, 08:57 PM
Yeah, because it needs to return a result, which is what the loop looks for. Also, it needs to have a boolean result.
Procedures don't return results, functions do, and thats why WeAreFull would need to be a function.

Hope that helps =)

Johura
10-02-2007, 11:43 AM
Great tut, +rep. Your now on my respected list :p

Losmoges
11-02-2007, 03:08 PM
Nice work, really helped me with my first script, which will be online soon.

cookman213
11-03-2007, 07:27 PM
this helps alot and was very easy to understand, you broke it down very nicely.

Pancakes
11-03-2007, 11:01 PM
Thanks for your comments everyone. Look out for a short tut by me soon on FindObjCustom.

wafyl
11-05-2007, 09:56 PM
Thanks, this really helped!

WT-Fakawi
11-05-2007, 10:34 PM
Cupped. Good Work. Could need some more Headers, reads better, but I am not complaining....


;)