REXX for Newies:Part IV

Автор: Chris Wenham

Дата: November, 1998

Источник: EDM/2

 

Summary: In the fourth installment of our series we teach how to do repetitive jobs by using simple loops.

Consider the act of pouring a cup of coffee for your guests. Assuming it's brewed, you fetch a cup, pour the coffee into it, add the creamer, add the sugar, stir and give to your guest. For each guest the process is pretty much the same, with slight changes where one guest prefers black coffee, or extra sugar, or no sugar, or whatever. If you have five guests, you repeat these same basic actions five times. From the point where you fetch the cup to the point where you hand over the cup can be thought of as the beginning and end of a loop - a procedure that can be repeated with little variation and predictable results. Written in English, the coffee-pouring loop might look like this:

  1. Fetch a cup
  2. Pour coffee into cup
  3. If guest wants creamer, then add the creamer
  4. If guest wants sugar, then add the sugar
  5. Stir
  6. If guest insulted my mother then spill coffee in his lap, else deliver the cup to the guest
  7. Go back to step 1 if there is another guest waiting.

The important part is step 7, the one that closes the loop yet still provides a means of exiting it. If there's still another guest waiting for coffee, then repeat the procedure, and if not, then the loop is ended and you can switch off the coffee maker. You can see how much more elegant this is than the alternative, which assumes you have 3 guests with differing tastes:

  1. Fetch a cup for guest #1
  2. Pour coffee into cup
  3. Add creamer
  4. Add sugar
  5. Stir
  6. Deliver cup to guest #1
  7. Fetch a cup for guest #2
  8. Pour coffee into cup
  9. Add sugar
  10. Stir
  11. Spill coffee in guest #2's lap
  12. Fetch cup for guest #3
  13. Pour coffee into cup
  14. Add creamer
  15. Stir
  16. Deliver cup to guest #3

Sixteen steps instead of 7. Hmm, which is the more efficient set of instructions? Which would definitely be the most efficient set if you had twenty guests? While the second method spelled out in concrete terms that guest #1 liked his coffee with cream and sugar, guest #2 liked only sugar (as well as needing a lesson in manners), and guest #3 liked only creamer, it would be trivial to simply ask the "if" question of yourself or the guest each time you poured a new cup.

Of course, none of us really need a list of written instructions to make a cup of coffee, but consider if it had been a repair manual for lawnmower engines - where some model engines have a slightly different housing or primer assembly? Each engine is repaired in pretty much the same way, but a concise list of instructions is still needed if you're to put an unfamiliar machine back together properly. The repair manual could redundantly spell out the instructions for each and every slight variation in the manufacturer's line, or it could write one set of instructions and insert lines such as "If this is a model G120, then remove the left retaining screw first" in appropriate places. The manual becomes thinner and easier to maintain for future models.

In Rexx programs, loops such as these are one of the single most powerful and fundamental techniques to learn. You cannot really write even medium sized programs without them as you'll find the need to do essentially the same series of tasks an arbitrary number of times will crop up time and time again. Computers were invented to do these kind of repetitive tasks after all.

In Rexx, loops are started and configured around the "do - end" instruction pair. "Do" marks the beginning of a block of instructions, it'd come just before step 1 of our coffee making procedure above. "End" of course marks the end of the block of instructions. Here's an example:

/* Do something, man! */
Do
  Say "This is the first instruction within a do-end block"
  Say "This is the second instruction within a do-end block"
End

The output of this program when run is:

[C:\]do
This is the first instruction within a do-end block
This is the second instruction within a do-end block

The Do and End instructions alone don't add anything to this program, but watch what happens when you make one measly little change to it; adding the number 3 after Do:

/* Do something, man! */
Do 3
  Say "This is the first instruction within a do-end block"
  Say "This is the second instruction within a do-end block"
End

You get this when the program is run:

[C:\]do
This is the first instruction within a do-end block
This is the second instruction within a do-end block
This is the first instruction within a do-end block
This is the second instruction within a do-end block
This is the first instruction within a do-end block
This is the second instruction within a do-end block 

Woah, magic. Just putting a number after the Do instruction made it repeat everything within the Do-End pair that many times. If you've typed this instruction in yourself, try putting 100 after the Do instruction, and watch things scroll off the top of your screen.

The next logical step is to put in a way to find out how many times we've looped. We do this by throwing a variable in the mix to keep track. While the Do instruction has a counter built into it to keep track of things for itself, we can explicity say what we want this counter to be called, what number we want it to start with, and what number we want it to end on. Here's the same program as above, but with one more addition to the Do instruction and one more Say instruction so we can watch what's happening:

/* Do something, man! */
Do counter = 3 to 5
  Say "We're in loop number" counter
  Say "This is the first instruction within a do-end block"
  Say "This is the second instruction within a do-end block"
End

Running our program now will give us this kind of output:

[C:\]do
We're in loop number 3
This is the first instruction within a do-end block
This is the second instruction within a do-end block
We're in loop number 4
This is the first instruction within a do-end block
This is the second instruction within a do-end block
We're in loop number 5
This is the first instruction within a do-end block
This is the second instruction within a do-end block

What we did is expand the Do instruction to explicitly name the variable as counter, tell it what number to start on (3) and what number to count to (5).

Since counter is a variable just like any other, we can use it in our new Say instruction to tell us where in the series of loops we are. If you're wondering why you'd want to start at a number other than 1, think of a program that adds page numbers to a book or presentation. The copyright notice page and table of contents might be un-numbered, but still considered as pages 1 and 2 respectively - meaning that you'd want to start page numbering at 3 instead of 1 when you got to the actual content.

There are no restrictions to what you can put inside of the Do-End pair. Any valid Rexx code goes, everything that you've learned to date and everything you will learn (with one or two exceptions, but we'll note those when we come to them). Indeed, you can even put more loops within the block, for example:

/* Rows and columns */
Do row = 1 to 3
  Do column = 1 to 3
    Say "Row" row "and Column" column
  End
End

Would produce this when run:

[C:\]do
Row 1 and Column 1
Row 1 and Column 2
Row 1 and Column 3
Row 2 and Column 1
Row 2 and Column 2
Row 2 and Column 3
Row 3 and Column 1
Row 3 and Column 2
Row 3 and Column 3

Only one Say instruction in the whole program, but it gets used 9 times. 3 loops of 3 loops, do the math. Useful stuff, especially when it comes to things like filling out tables and calculating seating arrangements.

In part V, we briefly cover the PARSE command (which we touched on earlier), how to call functions, and at last, our first example of a practical Rexx Macro that controls another application!