Software Design Using C++Repetition in C++IntroductionComputers excel at repeating sections of code over and over. They can very quickly do thousands of similar computations, one after the other. (On a computer with more than one processor, several computations may even be able to be done at the same time.) The main way to repeat a section of code in a program is with a "loop" construct. We sometimes speak of looping through a section of code when we are talking about repeating it over and over. The FOR LoopThis type of loop is usually used when you know exactly how many times you wish to execute the same section of code. Here is a simple example:
This loop is executed as follows. First,
The general form of a FOR loop is shown below. It has an initialization step, a condition, and an update step, all inside of the parentheses. The statement(s) to be repeated (the loop body) follow this. Note that the loop body is indented by 3 spaces to make it clear what section of code is being repeated. If there is only one statement in the loop body, the curly braces are not needed. If there are two or more statements in the loop body, the braces are necessary so that the statements are seen as a single block. The opening and closing braces should line up vertically, as yet another way to make it clear what section of code is getting repeated. (There are other indenting styles, but the vertical alignment of braces will be used consistently throughout these Web pages.)
The CONDITION is obviously a true/false condition. If the condition is omitted, it is taken as constantly true. The initialization and update steps can also be omitted if desired. The execution of this loop is best summarized in a flow chart (a type of follow-the-arrows diagram):
The way this works is as follows: First, the INITIALIZATION step is done. Then the CONDITION is checked. If the CONDITION is false, the loop is finished and we proceed to whatever follows the loop. Otherwise, the LOOP_BODY is executed. Always after doing the LOOP_BODY, the UPDATE step is executed. Then we "loop back" and check the CONDITION. If it is false, the loop is over. Otherwise, we do the LOOP_BODY followed by the UPDATE step. This is repeated as needed. If the CONDITION never is false when checked, the loop runs forever! Although the main use of a FOR loop is in creating a counting-based loop like our example above, other things can be done with this type of loop. As a nonsense example, consider the following:
Can you predict what this loop does? If you need help, look at the flow chart.
Since the initialization step is empty, the first thing that happens is that
the condition is checked. Since that is also empty, it is taken to mean true.
Therefore we execute the loop body, printing
It's time to look at a complete example. The
roots1.cpp program uses a FOR loop to count from 1
to 20, each time printing the number and its square root. Notice that the
program sets up a constant named Newer compilers may require you to convert the parameter of the sqrt function to a double instead of leaving it as an int. Thus you may need to use something like this:
If you run the above example, you will see that the numbers
don't line up nicely into vertical columns. To do that you need to use
some fancy formatting as in roots2.cpp. It uses
This program loops from 1 to 100, displaying the square roots of each. Since each square root is printed on a new line, that is more output than can fit on the screen. If nothing were done to handle it, this output would quickly scroll past, so that the user would only be able to read the last 20 or so lines of the output. Our program handled this problem by placing the following inside of the loop:
This is a bit tricky! The % sign indicates the mod operator (integer
remainder). So, each time around the loop we are checking the remainder
when The WHILE LoopThe WHILE loop repeats a section of code as long as some condition is true. Of course, the condition is only evaluated at a certain point, as will be clear after we draw the flow chart for this type of loop. In outline, a WHILE loop looks like this:
The flow chart is shown below. Note that the condition is evaluated when we first reach the WHILE loop. If the condition is false, the loop is skipped. If the condition is true, the loop body is executed, and we then go back to testing the condition, etc.
The following is a simple example of a WHILE loop. It starts
Look at the grade3.cpp program as a more practical example. It prompts the user to repeatedly enter number grades, printing out the corresponding letter grade for each. In order to end the program, the user enters the nonsense grade of -1. The following is an outline of the structure of the WHILE loop used in the main function. Minor details are omitted in order to make the overall structure clearer.
Before we reach the loop we read in the first numeric grade. As long as it is not the special ending value -1, we call a function to convert the grade into a letter. That letter grade is then printed on the screen. Before looping around to try the condition again, we read in a new numeric grade so that we test a new value each time around the loop. The only way that the loop will end is if the user enters a -1. This special -1 value is called a sentinel. A sentinel is any special value used to mark the end of data. The WHILE loop above is just one example of what is called a "sentinel-controlled WHILE loop". This pattern is used so often that it should be memorized so that you do not have to reinvent it. In general the pattern looks like the following, which is written partly in English and partly in C++:
There are several well-known loop patterns that you should know. See the Programming Patterns section of these Web pages for more information.
In our example program there is a somewhat mysterious use of
The above program also uses a function that has both a parameter and a
return value. The "Given" section of the comments on the function tell us
that the parameter The DO..WHILE LoopThis type of loop also repeats something as long as a condition is true. However, the condition is tested after executing the loop body, not before as with a WHILE loop. One consequence of this is that the loop body is always executed at least once in a DO..WHILE loop, but the loop body may be entirely skipped in a WHILE loop. Below we have an outline for a DO..WHILE loop and its flow chart:
A practical example of this type of loop can be found in the grade2.cpp program. The DO..WHILE portion looks like this:
The 4 lines of code in the loop body are repeated as long as
If you look inside of the
You might wonder why we did not read in the newline with
Properties of Loops--> LinkThe above link gives you a short Web page summarizing the main properties of our three types of loops. It tells you whether the number of times to do the loop body must be known in advance, the minimum number of times that the loop body can be executed, and whether the loop can give an infinite loop. Some Dark GDK ExamplesNow that we have moved through the topics of conditionals and repetition, we can now create some slightly more interesting Dark GDK Programs. First, let us look at a rather basic 3D application. This program merely creates a cube and moves it back and forth on the screen. For the time being, trust the comments and code regarding how 3D objects are rendered and viewed. The important part for what we are learning is the main GDK event loop:
In this loop, you can see that we will move forward the cube (which has id = 1, used as the first parameter for dbMoveObject) a specified distance. As will be seen in a moment the variable "frameMultiplier" has a value of either 1 or -1 and the variable "stepValue" is 0.5. Therefore, the object is moved either (1 * 0.5 = 0.5) or (-1 * 0.5 = -0.5), depending on the value of "frameMultiplier". After each frame is synchronized, we increment the value of the current "frameNum" and evaluate the next, very important, conditional. The conditional checks to see if (frameNum % 90) equals 0. If we recall from modular arithmetic, this is true for any integer multiple of 90. (e.g. 0, 90, 180, 270). Therefore, on every ninetieth iteration of this loop the frame muliplier is going to be set equal to itself times -1. This means that every 90th iteration, the direction will change for the cube on the next call to dbMoveObject. For a the whole program see CubeMove.cpp. The window of this program should appear like this. NOTE: This will have to be created as a Dark GDK - 3D Game Project. 2D Sprite Sheets
As another 2D example, lets look at an application that uses animated sprites.
However, before looking at the code, we should look at the basics of
sprite sheets.
Thus far, we have only utilized single-frame images for our sprites on the screen. Using this methodology,
we would have to keep track of multiple sprites if we wanted to use frame-by-frame animation in a program.
Luckily for us, Dark GDK allows us to store multiple frames of a sprite in a structure known as a
"sprite sheet" which can then be manipulated as though it were a sprite. It is merely a two-dimensional
grid of equally-sized rectangles which contain each frame for a given animation. For example, the following
206 x 78 pixel image is a six frame sprite-sheet for a 103 x 26 pixel image: When loaded appropriately, this image can be used to define a 6-frame animation of the given ship firing its basic-looking laser. Though the example may appear to be trivial, it utilizes the functions which are generally necessary for using sprite sheets. Let's pull apart the following code:
The parameters for the "dbCreateAnimatedSprite" function are rather intuitive. The first and last are the SpriteID and ImageID respectively and the second parameter is obviously the relative name of the image used as a sprite sheet. Parameters three and four define the grid size for the given sprite sheet. For our example, we are using a sprite with two columns and three rows. As you can tell, however, the sprite has not been placed on the screen. This is done later with the "dbSprite" function call. However, before that call is "dbPlaySprite" which is used to manage the process of cycling through sprite animations frames. The first parameter is the SpriteID indicating which sprite is to be animated. The next two parameters are the start and end frames for our animation sequence. You need not be bound to the entire sprite sheet when animating. One sprite sheet can contain many various animations, so long as each cell has the same dimensions. The final parameter is the frame-delay in milliseconds. The handling of the animation is done internally within the Dark GDK rendering environment, so this method does not stall the game. Based on previous animation function calls, this will progress the sprite to the appropriate frame after time has elapsed. It must therefore be called each loop to evaluate whether or not the sprite is to be altered. As will be seen in our example, this will allow us to have animations with different delays within the same rendering scene. For an example of such a program, see AnimatedSprite.cpp and download the code with all of its dependencies in AnimatedSprite.zip. A Beam Reflecting ProgramAs another example, let us look at a program which outputs a small line (a "beam") which travels up and down the screen. Each time the beam reaches the edge of the screen, the program plays a cow-bell sound. The code for this program is found in BasicLineBounce.cpp. The bulk of our logic is in the main event loop and follows a finite-state machine approach to the logic managing the movement of the beam. We will only consider it in high-level terms since such machines are the topic of later work in CS 170, Discrete Structures.
A light beam is defined as having one of the following states:
In order to compile this application, you will need all of the files in BasicLineBounce.zip. As one final example, let us look briefly at a simple program which generates a Sierpinski Gasket, which is a type of fractal. Using one method of generating the gasket, namely the "Chaos Game" algorithm, we choose three random, non-linear points in the 2D plane (i.e. we make a triangle). Next, we choose any point within the triangle and call it p. Then, we find the midpoint between p and any one of the three vertices (randomly selected), storing that point in p. After drawing p, we iterate again until we reach our desired count of iterations. This leaves us with this image. The code can be found in Sierpinski.cpp Related ItemProgramming Patterns Back to the main page for Software Design Using C++ |