REXX for Newies:Part III
Автор: Chris Wenham
Дата: November, 1998
Summary: The third part in this series teaches how to do simple arithmetic in Rexx, and why it's good for more than just boring math
"Why do we need math when we have electronic scoreboards?" - Character in the "Grin and bear it" Sunday comic strip.
100 years ago, a "Computer" was a man who spent his work life with a hundred others just like him in a big hall, doing mind-numbing arithmetic all day in order to balance the company books. Then a bunch of eggheads at Bell Labs invented the transistor and all the human "computers" got fired, only to learn Cobol a while later and get their revenge by coding the Y2K bug into almost every semiconductor-based device ever built. This funny anecdote leads us to this month's topic: Arithmetic in Rexx, what computers were originally intended for. Math stuff.
Chances are you're going to need to do basic number crunching in your scripts and macros, even if your task isn't actually mathematical in goal. It's needed for simple counting jobs (how many words are in a file maybe) calculating what part of a string to snip out (perhaps to get a stock quote or a name), and controlling the flow of a program (you might want it to ignore every 3rd file in a list, or delete a piece of e-mail if it contains the word "FREE!" more than twice). Arithmetic is just one of those things you're going to have to learn, but fortunately, you only need to learn how to phrase the problems, the computer does all the hard stuff.
To do all the basic mathematical operations in Rexx is easy because nearly everything is written as you would write it down on a piece of paper. Here's an example that you don't need to type in:
/* Two plus two */ Say "Two plus two equals:" 2 + 2
Since the 2 + 2 part is outside the quotes, Rexx performs the calculation and prints "4" as the answer. Since you learned how to use variables in Part 2, here's an example of the same program written slightly differently:
/* Two plus two */ Answer = 2 + 2 Say "Two plus two equals:" Answer
What's different is that we performed the calculation and stored it temporarily in a variable called Answer. Since the variable stores a number, we can perform arithmetic using the names of the variables as well as the numbers themselves. For example:
/* Two plus two times two */ Answer = 2 + 2 Answer = Answer * 2 Say "Two plus two, times two equals:" Answer
Notice the "Answer = Answer * 2" line, there's two new concepts here. First is that we can use the names of variables in mathematical formulas and Rexx will substitute them for their stored values when the program is run. Secondly, the calculated result of the multiplication is not stored in the variable that comes before the equals (=) sign until everything after the equals sign has been computed. So, we can recycle the variable if we want. Here's what's happening in the computer's memory if you're confused:
The code as you typed it:
Answer = Answer * 2
Rexx now works on what comes after the equals sign, substituting the variable names for their stored values:
Answer = 4 * 2
Now it solves the problem:
Answer = 8
And finally it stores the value of 8 in the variable called Answer, overwriting the value of 4 that was previously stored there. It does not add 8 to 4.
This is a simple convenience, but it lets us recycle variable names without having to create temporary place-holders all the time. We could have used a line such as:
Answer = Answer + Answer * Answer - Answer / Answer
And Rexx would have worked it out to mean this:
Answer = 4 + 4 * 4 - 4 / 4
The symbols used by Rexx for basic arithmetic are universally shared with almost every other programming language in existence. In school you might have been taught to use an 'X' as the multiplication sign and the horizontal line with two dots as the division sign. But since the 'X' could be mistaken for a variable name, an asterisk (*) was used instead. And since there's nothing on the traditional keyboard layout for the division symbol, a forward slash (/) is used instead. Not surprisingly, you can't use any of these symbols (+, -, * or /) in the names of your variables, even if you don't put any spaces in them.
One interesting note about the division sign. There's actually three you can choose from, each performs a slightly different function. The standard forward-slash will do a plain and straightforward division. The result of 7 / 2 in Rexx is 3.5. But then there's the percent sign ( % ) that performs the division, but only gives you the whole number portion of the result, forgetting the remainder. 7 % 2 would give you 3 as the result instead of 3.5. The opposite of the percent sign is the double-slash ( // ) which performs the division but only gives you the remainder, forgetting the whole number portion. 7 // 2 would give you 5.
This double slash thing is often used to tell whether a variable stores an odd or an even number. If you divide by 2 and it returns 0 as the remainder, then you know it's an even number. Anything else and you know it's odd. For example:
/* Is this an odd or even number? */ Say "Please give me a number" Parse Pull TheNumber if TheNumber // 2 = 0 then say "This is an even number" else say "This is an odd number"
Save this as "oddeven.cmd" and run it a few times, giving different numbers each time. I happen to use a test like this in the script that helps assemble OS/2 e-Zine!'s pages to decide what kind of advertising banner should run along the top. Odd pages get two small ads, even pages get the full sized ads. Assume the page right after the Table Of Contents is #1 and see for yourself (pages without advertising aren't counted in the numbering). One tiny little chore automated with the help of a little arithmetic.
Now be rebellious and run the program again, typing your name instead of a number. Notice something? The program crashed, showing you what the line was that it crashed on and why. "Bad arithmetic conversion" it said. What happened is that Rexx trusts you. If you program it to expect numbers to be typed in, it'll assume so and won't question you until it actually comes time to do mathematics and other number stuff on them. It said "Bad arithmetic conversion" because Rexx stores the contents of every variable as a string, converting them from a couple of characters glued together into meaningful values only when it actually needs to perform arithmetic on them. If, as it tried to convert your name into a number in anticipation of performing division, it actually finds out that the variable doesn't have a number after all, then it stops the program and complains. Up until that point it assumes everything is groovy.
You've got your first taste of how to debug a program there. Rexx, being an easy language, is very helpful in the way it reports problems. It'll give you the line number, it'll give you the context (more on that when we come to loops and procedures), and it'll give you the actual line of code itself with a fairly descriptive reason why the program was stopped. This makes it easy to track down the right line of code in a big program, and tell if it was just a typo or a more serious design flaw. We'll discuss more about debugging in another issue.
Coming up in Part IV is a look at looping, where things really start to get powerful. Soon we'll also get into the useful stuff where we write simple macros that control a larger application.