prev | toc | next
 

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