3.2 Overview: MUF (cont'd)
Stack Effects and Keeping Your Data:
Recall that when we did me @ "Hello, world!" notify, the
NOTIFY `used up' the dbref and the string. This is formally
expressed in NOTIFY 's `stack effect comment'. Type
man notify to see the manual entry for NOTIFY.
The entry, like all entries for primitives, includes a stack effect
comment: ( d s -- ) .
The stack effect comment provides a `before and after synopsis' of the
primitive's effect on the stack: the items before the dash are what the
primitive requires; the items after are what it leaves. (`d'
means `dbref'; `s' means `string'; `i' means
integer; `f' means floating point number; `v' means
a variable'; `x' means an item that can have various types.) In
this case, NOTIFY needs a dbref and a string in that order
and leaves nothing in their place.
Most primitives `use up' their data in this fashion, and this is a
Good Thing. If they did not, programmers would need to put one or
several POPs after each primitive to keep the stack from
growing unmanageable, which would greatly increase the size of programs.
However, this also means that when you are working with data that you
will need again later in the program, you will need to store it, either
by putting a copy of it on the stack or by storing it in a variable.
The DUP primitive makes a copy of the top item on the
stack; its stack effect comment is ( x -- x x ). The
following version of our program uses DUP to make an extra
copy of the random number, and after the IF-ELSE-THEN
section it tells the user what the random number is.
====================================
: main
random dup 1000000 > if
me @ "Yes, the number is greater than one million." notify
else
me @ "No, the number is less than one million." notify
then
intostr me @ swap notify
;
====================================
This time, because of the DUP in the first line, there
are two copies of the random number beneath the 1000000
when the greater-than test is executed. The test will use one of them,
but the other will remain on the stack. The IF-ELSE-THEN
part will then execute, leaving this number unaffected.
The last line tells the user what the random number was. When the program
gets to this line, the only thing on the stack is the random number, in
integer form. The stack effect of NOTIFY is ( d s --
), so we need to do a little rearranging: we need to use some `stack
handling' primitives to convert the integer into a string and place the stack
items in the correct order.
INTOSTR converts the random integer into a string. If
the random integer were 23231874, INTOSTR
would remove this number from the stack and leave the string
"23231874" in its place. me @ puts the user's
dbref on the stack. But now the two stack items are in the wrong order.
The dbref needs to be in front of (or `below') the string.
SWAP, one of several stack handling primitives, reverses
the order of the top two items on the stack ( x y -- y x ).
So, if our stack were `"23231874" #123', SWAP
would make it `#123 "23231874"', which will work for
NOTIFY.
Instead of duplicating a datum on the stack, you can also store it in
a variable. This is especially useful when the datum will be needed much
later in the program, or when the program will need the same datum at
different places. There are two steps to storing data in this way:
declaring the variable, and storing the data.
Declaring the variable is done simply by including the word
var or lvar followed by the name of the
variable in the program. The variable can be declared anywhere in the
program, so long as it's somewhere before the variable is used
in the code, and outside the definition of a function. So, it
makes good sense to declare all your variables at the top of the
program. Var declares a global variable (all programs can
use it); lvar declares a local variable (only this program
can use it). Use local variables. The data type of a variable
does not have to be declared: once you define a variable, it can hold
any type of data.
(Note: Version fb6.0 and following of the
MUCK server support `scoped variables'... that is,
variables that only exist within certain sections of code. On
fb6.0 and later, you can can use lvar within a
function: the resulting variable will only be available within that
function, and will be used in place of variables with the same name that
exist at a higher, program-wide scope.)
Once a variable has been declared, you can store data in it with the
`store' operator, an ! exclamation point ( x v --
). The following version of our program does the same thing as
the previous one, but this time stores the random number as a variable
instead of keeping it on the stack.
====================================
lvar ourNumber
: main
random ourNumber !
ourNumber @ 1000000 > if
me @ "Yes, the number is greater than one million." notify
else
me @ "No, the number is less than one million." notify
then
me @ ourNumber @ intostr notify
;
====================================
prev |
toc |
top |
next
|