| Stack Notation | Lesson 3 | Contents |
Before we go further, you should become acquainted with a special notation that tells someone reading your program listing what's happening on the stack before and after a command.
The format is:
( before -- after )
The arrangement of values on the stack is shown both before and after the operation (note the space between the opening parenthesis and the start of the description). The actual operation is implied by the double-hyphen. Therefore, in an addition operation (just the + operation, not the extra stuff to display it and move the Mops prompt) you have two numbers on the stack before the operation, and you end up with a single number, the sum of those numbers, on the stack after the operation. That is, you start with n1 and n2 on the stack and end with the sum on the stack.
The stack notation looks like this:
( n1 n2 -- sum )
This, therefore, is the description for the addition operation.
For the dot command, the description is:
( n -- )
because this command takes the topmost value from the stack and displays it on the screen. The value is removed from the stack in the process, leaving no trace of it after the operation.
In the CR command there is nothing happening to values in the stack, it simply moves the prompt to the left margin of the next line. Because no stack operations are involved, the CR commands notation is:
( -- )
The definition of every Mops word you define in a program should be accompanied by its stack notation. Our convention is that we may omit the stack notation if it is ( -- ), but only in this situation.
Peruse the Predefined Classes in Part 3 of this manual to see how we have noted the stack actions of all the words in the Mops dictionary. While the notation will at first help you learn how Mops words work, it will also help you later when you start writing programs in an editor. The words and numbers in parentheses (with at least one space after the opening parenthesis) are not compiled into the program, so they won't add one byte to the size of your final program. The notations are there to aid you in tracing your program if you run into a snafu during program development.
All in all, the stack notation is a handy shortcut to documenting your programs.
Note: Since anything in parentheses (i.e., starting with an open parenthesis followed by one or more spaces) is ignored by Mops, you don't have to type stack notation for words you define at the Mops prompt. Stack notation is strictly an aid for reading source code. In this tutorial, we often show the stack notation for words you define. The notation is presented to help you better understand the definition and show you how your definitions should look once you begin writing your own programs in an editor.
Here are Mops stack descriptions of the four basic arithmetic operations:
| + | ( n1 n2 -- sum ) | Adds n1+n2 and leaves the sum on the stack |
|---|---|---|
| - | ( n1 n2 -- diff ) | Subtracts n1-n2 and leaves the difference on the stack |
| * | ( n1 n2 -- prod ) | Multiplies n1*n2 and leaves the product on the stack |
| / | ( n1 n2 -- quot ) | Divides n1/n2 and leaves the quotient on the stack |
To newcomers, the stack order (the way in which numbers come out in the reverse order) may be confusing when it comes to subtraction and division, because the order of the numbers is critical those operations. If you want to subtract 4 from 10, you want to make sure that those numbers come out of the stack in the correct order for the subtraction operation to work on them. Fortunately, Mops saves you from performing all kinds of mental gymnastics in the process.
In the kind of arithmetic notation you learned in school, you write the problem like this:
10 - 4
and get the desired answer, 6. In Mops arithmetic, the order of the numbers going on the stack is the same. All you do is move the operation sign to the right.
The problem becomes:
10 4 -
The same goes for division. The formula for dividing 200 by 25 morphs from
200 / 25
to
200 25 /
The four basic arithmetic operations are usable only on integers, that is, whole numbers like -2, 0, 3, -453, and 1002. Numbers with digits to the right of the decimal don't count. Don't worry, however, because Mops has plenty of ways to handle all kinds of numbers, as you'll see later on.
Experiment using the four simple arithmetic operations. Place one, two, three, and four integers (or more if you like) in the stack to understand how the operations make use of the numbers in the stack. Try them out now, and pay special attention to answers to division problems.
Everything should have worked well, except when you divided numbers that were not even multiples of each other. For example, if you divide 10 by 3, the Mops answer is 3.
10 3 / . CR
3
When you use the divide operation (/) in Mops, the remainder is lost forever. But Mops has two other operations that take care of the remainder for you.
| /MOD | ( n1 n2 -- rem quot ) | Divides n1 by n2 and then places the quotient and remainder on the stack. |
|---|---|---|
| MOD | ( n1 n2 -- rem ) | Divides n1 by n2 and then places only the remainder on the stack |
Try out the 10-divided-by-3 example again, but this time using the /MOD operation instead of straight division (Remember! Mops is case insensitive).
10 3 /mod . . cr
3 1
Notice now that both the quotient (3) and remainder (1) were returned to the stack (and subsequently printed out by two dot commands). Notice also the order of the two answers as they came out of the stack and how the order compares with the order of the /MOD stack notation above. The rightmost value in the stack definition, the quotient, was on the top of the stack and was therefore the first one to be printed out on the display.
Division involving negative numbers can be done in two different ways. In Mops we use the convention used by the Macintosh hardware, namely "towards zero" division. If the exact quotient isn't an integer, the quotient that the division operation gives will be the next integer towards zero. For example, -10 divided by 7 will give a quotient of -1, with a remainder of -3.
The remainder will always have the same sign as the dividend (the first operand), unless it is zero.
Postfix Notation
If you're not particularly well versed in this reverse notation, called postfix notation or
RPN
then it is important to recognize that complex math formulas need to be analyzed before they can be entered into Mops's postfix, integer arithmetic environment. For example, you may find yourself confronted with having to include the following formula in a Mops program:
1.25 * 12 * 50
10
If so, then Mops' integer arithmetic might seem like a stumbling block, and its postfix notation may seem worthless. But call upon simple algebra to convert everything to integers, and break up the complex formula into the same steps you would use to solve it with a pencil and paper.
The Mops equivalent of this formula is:
5 12 50 * * 40 /
It's worth following what happens to the stack during a complex formula like this. First of all, to make the 1.25 an integer, multiply it and the denominator by four. Then put all three numbers to be multiplied into the stack. The first multiplication operation multiplies the topmost two numbers (50 times 12) leaving the result (600) on the stack. That leaves 600 on the top of the stack, and 5 below it. The second multiplication operation multiplies the two numbers remaining on the stack (600 times 5) and leaves the result (3000) on the stack. This result is the dividend (numerator) of the division about to take place. Now it's time to put the divisor (40) on top of the stack. Then the final operation, the division, divides the two numbers in the stack.
Don't be discouraged by all this concern over the stack, you'll learn in a later lesson that Mops provides you with two powerful tools named input parameters and local variables that let you substitute readily identifiable names for the values on the stack and use them at will. The stack will become almost invisible to you. It is important, however, to understand the stack fundamentals just the same.
Stack Manipulations
While named input parameters and local variables will disguise many stack manipulations for you, there may be occasions when the order of items in the stack requires an explicit move of some values for a particular operation. Conversely, the stack may have a number on it that you simply don't need anymore, and want to dispose of. In these cases, you can choose from a series of stack manipulation operations.
Here are three stack manipulation operators that you should keep in mind:
| SWAP | ( n1 n2 -- n2 n1 ) | Switches the order of the topmost two items in the parameters stack |
|---|---|---|
| DUP | ( n -- n n ) | Duplicates the topmost stack item and places the new copy on top |
| DROP | ( n -- ) | Removes the topmost stack item. If another item is next in line, it becomes the topmost item. |
SWAP is used, for example, in a more complex definition, when two values are on the stack but their order is wrong for a subtraction or division. In fact, it could have been used in a less elegant definition for the problem cited in Lesson 3.
5 * 12 * 50
40
By putting the divisor at the bottom of the stack (the first one in), you can perform all the multiplications and then switch the order of the two remaining numbers on the stack so they divide properly.
The revised operation would be:
40 5 12 50 * * swap /
The word definition that calculates this would be:
: formula ( denom num1 num2 num3 -- solution )
* * swap / ;
DUP is sometimes useful for particular arithmetic applications. An example of how DUP works is to use it to calculate the square of a number. Instead of pushing two identical values on the stack, you can push only one, duplicate it, and then multiply the two values on the stack like this:
4 dup *
Calculating the cube of a number could, likewise, be performed like this:
4 dup dup * *
Therefore, you could set up a Mops word "cubed" to perform the cube calculation:
: cubed ( n -- ) dup dup * * . cr ;
Then you could type "3 cubed" from the Mops prompt, and the answer would appear on the screen like this:
3 cubed
27
Experiment with the other stack manipulation operators described above. Place a few numbers in the parameter stack, issue the commands, and see what happens in the stack display. If you need to, you can combine two or more stack manipulation operators in the same Mops word definition as your arithmetic needs arise.
But overall, named input parameters and local variables are the preferred way of handling values on the stack in a complex definition. Tracing and debugging a program is much easier than with explicit stack manipulations, or so-called stack gymnastics. And because named parameters and local variables are more intuitive, there is less chance of making a mistake in the first place.
| Previous Chapter | Contents | Next Chapter |
|---|---|---|
| ⇧ | ||
| This page online: http://PowerMops.com/MopsManual/Lessons/Chapter3.html | ||