VOICE Home Page: http://www.os2voice.org |
July 2003
[Newsletter Index]
|
By Thomas Klein© July 2003 |
Sorry for the lack of last month's issue, but I was busy renovating our bathroom. Itching fingers and extreme fatigue in the evening hours kept me from typing. First let me mention, that there was some severe error in the previous issue concerning the say command:
And - by using a final example - using the commandThis is complete rubbish I'm afraid. ;)(with 7 spaces between the literal and the variable name) will result in an output of say "The meaning of life is:" molThe number of spaces used in the say command is exactly the same that is used in the output formatting, which is not typical for programming languages in general AFAIK. Usually, the command is parsed into a keyword and parameters part only and the number of spaces between them is ignored. The meaning of life is: 42
I could have stated this to be some sort of test <g>, but being honest I have to admit it was just an error... ;) Nevertheless, the other examples were right. The meaning of life is: 42
Now, don't ask me how come that I write such stuff or why I seemed to get such output from my own testings. I simply don't know. Obviously something went wrong that day. The proof readers either didn't notice the glitch as well or seemed to be too afraid to mention. We're all just humans after all and we make mistakes. Sorry for this one.
A Sequence is what we already know from previous examples. The program flow is - graphically speaking - preceding from top to bottom, one statement (or command) is executed after another, like in
Okay, that's a somewhat stupid program - but don't mind it. Its not mandatory to understand what length(strip(name)) does - just notice that this is a typical batch process made of various commands (inputs, outputs, functions). The flow of execution begins with the first statement and ends with the last one. Then, the program is terminated. say "please enter your name (and press the return key)..." parse pull name say "Hello" name letters = length(strip(name)) say "Wow - your name consists of" letters "letters."
The meaning of a conditional expression in terms of program sequence is pretty much the same like you coming down a hallway that ends up in front of two or more doors: Depending on a certain decision, you'll precede to walk through only one of them. You can't walk through more than one at the same time. To give you an impression of what this might look like:
This means, that the branches are mutually exclusive. say "please enter your name (and press the return key)..." parse pull name say "Hello" name letters = length(strip(name)) say "Wow - your name consists of" letters "letters." if letters < 5 then say "...which is quite short in fact." else say "...and that's rather long."
IF <conditional expression is true> THEN <command>
IF age < 20 THEN say "you're quite young."and
IF age < 20 THENand
say "you're quite young."
IF age < 20 THEN...are all valid notations with the same result. In addition, the case of the keywords doesn't matter. Within that scope, you're even free to space the single words as much as you want, like:
SAY "you're quite young"
IF age < 20 then(Note: the "then" was typed in lower case)
say "you're quite young."
ELSE
stuff:ELSE
is used to denote a command to be executed, if the condition does not apply. Thus, ELSE
can be thought of as a "separator between the branches". As ELSE
is an optional part of theIF
command, you don't need to code it if you don't want to do something
special with those cases that won't fit into the other branch. If there
is no ELSE
, there is nothing done about them.But hold it. If the whole thing is written on a single line, the parser might not get the point, like the following example might show: IF <conditional expression is true> THEN <command> ELSE <command2>
If the variable "age" is set to contain a value less than 20, the output would be: IF age < 20 then say "you're quite young." ELSE say "you're not THAT young anymore."
This is because the parser doesn't resolve the stuff "behind" the SAY command. To clarify, you either need to add a semicolon you're quite young. ELSE say you're not THAT young anymore.
or (the "better" way) you insert a line wrap after the first "command": IF age < 20 then say "you're quite young." ; ELSE say "you're not THAT young anymore."
Talking about multiple lines - the ELSE keyword and it's corresponding command can be written on the same line or separated by one or more empty lines (again, as much spaces involved as you want): IF age < 20 then say "you're quite young." ELSE say "you're not THAT young anymore."
To complete the examples in this stage here's the way that you should prefer in order to achieve better readability: IF age < 20 then say "you're quite young." ELSE say "you're not THAT young anymore."
This shows the two basic rules that apply: IF age < 20 then say "you're quite young." ELSE say "you're not THAT young anymore."
Let's summarize what we've learned about IF so far in matters of syntax:
Okay, let's move on: There's one more issue with the IF in REXX that you might not be aware of - especially if you have already done programming in another high-level language such as Basic or COBOL. REXX only accepts one single command within its branches. Except that you take care to explicitly name a block of commands by enclosing it in DO and END commands. Thus, the extension to the above syntax is that <command> or <command2> can be replaced by a structure of IF <conditional expression is true> THEN <command> ELSE <command2>
DO and END simply denote multiple commands to appear as a single block of statements to be included... to "make the parser understand". do command-2 command-3 ... command-n end
As a DO...END block is treated equally to a single command (from the parsers point of view), you're free to even "mix it to your needs":
or "the full monty": IF <conditional expression is true> THEN DO <command-1> <command-2> END ELSE <command-3>
To complete the variety of notations, there is one thing to note when using the "DO" keyword on the same line as the first command of the block (which you're free to do). You need to separate them by a semicolon. Okay, let's keep it short - the following code parts all have the same effect and meaning: IF <conditional expression is true> THEN DO <command-1> <command-2> END ELSE DO <command-3> <command-4> END
IF <conditional expression is true> THEN DO <command-1> .... END
IF <conditional expression is true> THEN DO <command-1> .... END
IF <conditional expression is true> THEN DO; <command-1> .... END
IF <conditional expression is true> THEN DO; <command-1> .... END
The best way of getting through this is to keep in mind the first of the samples above: "THEN, DO and <command> on a line of its own each". It will both look and read better!
Now to completely confuse you let me mention that a block of commands
enclosed by DO/END
can of course contain additional IF...ELSE...
structures
:)
Okay, this might become puzzling at first sight, but hey: Basically this works just like an onion: Each IF/THEN/ELSE
on its own is just like a single layer. And if you look at them on a per-layer view, they are all quite simple. No magic.
...
Just imagine that hallway which ends in front of two doors. Of course, after walking through one of them, there could be more doors again - this is what we call a "nested IF structure" - but basically, as I said, it's made up of the same parts again and again. Example:
Well okay - from the "meaning" point of view, that's a rather stupid program, but just to make you see: These are two IF statements "nested". The first one (the "outer one") has two branches (as always), but one branch contains another IF (an "inner" one). And this one uses a DO/END block within its own first branch by the way... say "please enter your name (and press the return key)..." parse pull name say "Hello" name letters = length(strip(name)) say "Wow - your name consists of" letters "letters." if letters < 5 then if letters < 4 then do call beep 500,100 say "...which is really short, man!" end else say "...which is quite short." else say "...and that's rather long."
Now let's see what I meant when saying "two or more doors":
The IF statement consists of an expression which is checked to be true. To make sure you get the point: The expression (regardless of how complex it might be) is either TRUE or NOT (that's to say true or false). So an IFStatement will provide you with "two doors" only. Take a look at the example above. If we would like to differ between "quite long" and "really long" names, it would mean to add another IF statement into the second branch of the "outer" IF... and sooner or later the whole thing will turn out to become difficult to read and might ease the creation of errors as it grows and grows.
Of course this is not a "wrong" way of programming, but maybe not the "best". We might need to check if there aren't other means of doing what we like to do. The case is that we don't want to check if something is true or not, but rather react upon different states that might happen to take place. In our (silly) example, we actually want to differ between several lengths of possible names. This can be accomplished by either creating a complex structure of nested IF statements (like we said above) or by using rexx's select statement:
This looks like - yeah! - what we've been searching for and actually is the solution to our problem, but it needs to be handled with care. What select does is checking the conditions from top to bottom and branches into the first one (within that order) which applies.say "please enter your name (and press the return key)..."
parse pull name
say "Hello" name
letters = length(strip(name))
say "Wow - your name consists of" letters "letters."
select
when letters < 4 then
say "...which is really short, man!"
when letters < 6 then
say "...which is quite short."
when letters < 9 then
say "...which is quite long."
otherwise
say "...which is really long, man!"
This means, that you need to keep in mind what it does when writing your cases... the following statement looks okay at first sight, but there's a minor logical glitch:
The problem is, that if the variable letters contains a value of 6, the first case (> 3) applies too... thus, the first branch will be executed and that's it. The second condition (>5) is true as well, but program flow won't come to this point. The otherwise keyword by the way defines a branch that is to be executed if none of the previous conditions applies - some sort of "else" in the select structure if you want.select
when letters > 3 then
say "...which is quite short, man!"
when letters > 5 then
say "...which is rather long."
when letters > 7 then
say "...which is really long."
otherwise
say "...which is really short, man!"
end
Another special thing is to mention when talking about rexx's select statement. You might know similar constructs from other programming languages, like the "evaluate" clause of COBOL or the "select case..." from Basic. These are quite similar in fact, but they only refer to different states of the same conditional expression. That's to say, the conditional expression itself is defines only once (at top of the statement) and then the branches refer to different states. Example from COBOL:
Nifty COBOL-programmers would have rather created a set of suited 88-levels below the "letters" variable in order to evaluate the truth of them, but that's another story... ;)EVALUATE LETTERS
CASE IS < 4
DISPLAY "...which is really short."
CASE IS < 6
DISPLAY "...which is quite short."
...
END-EVALUATE.
The difference with REXX's select statement is, that the condition to be tested is defined in each branch, thus, each branch can be used to test something completely different, whether this makes sense or not. Usually, you might want to differ between different states of the same expression (like different values of the same variable) but not test different things that have no "common ground" in matters of logic. There might be cases where it could be useful, but it's difficult to both read and understand the meaning behind it at first sight, like
This is some classic "argh!" construct that'll raise quite a few questions about what the heck it should be used for. Combinations like this one are usually nonsense, because one or more of the conditions could be true at the same time. But as you remember, only one door in your hallway can be passed through at a time...select
when age > 55 then
say "As a software developer you should remember punch cards, do you?"
when name = "Peter"
say "Hey, that's you again?"
when date('S') = "20040101" then
say "Happy new year!"
end
So what if "Peter" was entered, it's age is 57 and this program
is run on 01-Jan-2004?
Each condition applies, but only the first branch will be executed... in order to
be able create programs with (at least) quite "predictable behavior" you
should try to avoid such stuff. In these cases, a different coding is preferable
- like separated, consecutive IF statements. To conclude: Internally, the SELECT
statement is handled just like a compilation of single IF statements.
That's why the conditional expression is declared for each branch again. Keep in
mind to take care of it when using it - you'll get a sore spot for implementing
logical errors otherwise...
And finally, here's for the syntactical issues of the SELECT command (instead of some cute diagram again):
DO/.../END
block: If used, the DO
keyword can
be written alone on a single line or together with the first command.
In the latter case, you'll need a semicolon again to separate DO
from the first <command>
. This also applies to specifying them on the same line as the WHEN/.../THEN
stuff. Thus, the following parts of code are all valid notations and work the same
way:a.)Again, the first one (a.) is the one you should prefer to use: No hassle with semicolons, better readability, well... it depends on your "taste" in matters of self-explaining source code. The parser only checks for rules and doesn't care for stuff like indention - that's up to humans only... ;)
SELECT b.)
WHEN age > 55 then
DO
call beep 100,500
say "you're older than me!"
END
WHEN...
SELECT c.)
WHEN age > 55 then DO
call beep 100,500
say "you're older than me!"
END
WHEN ...
SELECT WHEN age > 55 then DO; call beep 100,500 d.)
say "you're older than me!"
END
WHEN ...
SELECT WHEN age > 55 then
DO; call beep 100,500
say "you're older than me!"
END
WHEN ...
NOP
To avoid ending this months part with nothing useful, let me present you with a "useful nothing" instead ;) - the NOP command. You might know that instruction from assembler and yes - the REXX counterpart does exactly the same: Nothing. Now isn't that funny? A special command used to "do nothing"... now "what the heck should that command be used for?" you might ask yourself... well, that's easy: Nothing. ;) It is not used to "do" anything but to "fill gaps" in visibility of logical structures.
NOP comes in handy whenever your nested, conditional expressions might
end up somewhere with a logical branch that actually isn't needed, because you just
want to take care of the "other" branch.
As we stated above, the ELSE branch of an IF statement is not
mandatory, so you actually are free to simply not code it... on the other hand -
especially when dealing with complex nested conditions, the source code simply might
"look clearer" if each IF has its ELSE. Even if that
ELSE simply does nothing.To give you an example:
By using NOP this would look like this:if age > 37 then
say "You're older than me!"
Now, if someone else is required to work with your code (or you are taking a look at it, months after you initially wrote the program) there is no need anymore to figure out if the ELSE was omitted by error or simply neglected... it's quite clear: ELSE NOP will show that expressively NOTHING should happen if the condition does not apply. Of course, NOP can be used with SELECT as well. Like in...if age > 37 then
say "You're older than me"
else
NOP
This code is almost self-explaining, there is no question arising from what is coded. Some programming languages will force you to think twice about how to construct your conditional expressions in order to achieve your goal, but wherever you'll have a NOP at hand, you simply don't need to care that much. ;)Select
when a > b then
say "There is a difference of" a - b
when a < b then
say "There is a difference of" b - a
otherwise
nop
end
Okay, that's enough code work for today! If you did manage to arrive down here without your head spinning - well, Sir, I'm proud of you. ;)
Next month, we'll talk about the third kind of flow type - iterations (or "loops"
if you prefer) - and how they are done in REXX.
References:
|
[Feature Index]
editor@os2voice.org
[Previous Page] [Newsletter Index] [Next Page]
VOICE Home Page: http://www.os2voice.org