'===========================================================================
' Subject: BASIC SUBROUTINES & FUNCTIONS      Date: 11-13-97 (06:30)       
'  Author: Judson D. McClendon                Code: Text                   
'  Origin: judmc@mindspring.com             Packet: FAQS.ABC
'===========================================================================
                     Basic Subroutines and Functions

Subroutines are useful little critters, and are used for two basic reasons. 
One is when you have a given set of instructions which you use at several
points in a program.  You can write the instructions one time in a subroutine
and call that subroutine whenever you want to execute those instructions. 
The other reason is to logically structure your program such that related
code is placed together.  This permits good, logically structured, top-down
design, and also facilitates creating libraries of pre-coded subroutines
which you can drop into a program whenever needed.

Subroutines are simple to create and use.  I will use QBasic/QuickBasic as an
example, but other languages are fairly similar  For simplicity I will just
use BASIC to refer to QBasic/QuickBasic.  In BASIC you can have 'informal' or
'formal' subroutines.  Informal subroutines are just blocks of code which are
invoked with the GOSUB command and that are terminated by a RETURN statement.
Like this:

   GOSUB MySub
   ...

MySub:
   <subroutine code>
   RETURN

These informal subroutines are part of the main module code, and share all
variables with the main module.

You create a 'formal' subroutine by setting off a block of code like this:

   SUB MySub
     <subroutine code goes here>
   END SUB

When you want to execute MySub, code this:

   CALL MySub

And the subroutine will be executed.  In languages like QBasic/QuickBasic
which have 'variable scoping', subroutines, by default, have their own
'local' variables.  This is good, because it lets you code the subroutine
without being concerned about trashing a variable of the same name used
outside the subroutine.  There are two mechanisms for the subroutine to
communicate with the rest of the program.  One is by passing 'parameters' to
the subroutine when you call it.  These parameters are defined in the
subroutine definition like this:

   SUB MySub(P1,P2)
     <subroutine code goes here>
   END SUB

Defined in this way, P1 and P2 are called 'formal parameters'.  When you want
to execute MySub, code this:

   CALL MySub(A1,A2)

A1 and A2 are called 'arguments', and are matched 1 to 1 with the 'formal
parameters' defined in the subroutine heading.  For the compiler to do this
matching efficiently, and to validate that your arguments match the formal
parameters in order, number and type, it needs to know what the formal
parameters are before it encounters the subroutine CALL.  This is done by
placing a 'subroutine declaration' at the front of your main module, like
this:

   DECLARE SUB MySub(P1 AS INTEGER, P2 AS DOUBLE)

This subroutine declaration is a template, telling the compiler the name of
the subroutine and the number and type of arguments which must be passed when
it is called.  It is the order, type and number of the formal parameters
which is important, not the names of the parameters.

The other method to permit the subroutine to communicate to the outside
program is to explicitly declare the scope of variables to 'cross' the
subroutine boundary.  From the outside program you can define a variable as
'SHARED', which means it can be seen from inside the subroutines.  In the
main module do this:

   DIM SHARED X AS DOUBLE

X can then be 'seen' and used inside subroutines.  This can be overridden on
an individual subroutine basis by explicitly declaring variables as STATIC
inside the subroutine:

   SUB MySub(P1,P2)
     STATIC X AS DOUBLE
     <subroutine code goes here>
   END SUB

Outside this subroutine, X is 'global', but X is also a separate 'local'
variable inside this subroutine.

You also can also do it the other way around.  You can declare a variable
as 'SHARED' from within a subroutine:

   SUB MySub(P1,P2)
     SHARED X AS DOUBLE
     <subroutine code goes here>
   END SUB

In this case, X is shared between this subroutine and the main module.  If X
is to be shared with another subroutine, the other subroutine must also
declare X as SHARED.

Now let's look at another useful construct, the 'function'.  Functions are
similar to subroutines, but have one specific difference: functions return
a value.  BASIC has a number of built in functions, like SIN(), ABS() and
INSTR$(), but you can 'roll your own' too.  BASIC supports two types of
user defined functions, the one-line function and the multi-line function.
One-line functions are defined at the beginning of your main module like
this:

  DEF FNTRIM$(X$) = LTRIM$(RTRIM$(X$))

The names of one-line functions always begin with FN, and they are always
in the form of an assignment expression, like this one which returns X$
with spaces trimmed from both sides.  You use it just like a built-in
function:

   A$ = FNTRIM$(A$)

Multi-line functions are defined similarly to subroutines, except that
functions have a variable type association which is the kind of value they
return.  The return type can be different from the parameter types.  The
function above can be coded as a multi-line function:

   FUNCTION TRIM$(X$)
      TRIM$ = LTRIM$(RTRIM$(X$))
   END FUNCTION

Note that the return value was assigned to the function name.  Multi-line
functions are used the same way as single line functions:

   A$ = TRIM$(A$)

Multi-Line functions, like subroutines, must have formal declarations:

   DECLARE FUNCTION TRIM$(X$)

But multi-line functions can have logic too.  Functions (and subroutines) can
even call themselves.  This self-referral is called 'recursion'.  Here is a
classic recursive function to calculate factorial (n!):

   DECLARE FUNCTION Fact& (N&)

   FUNCTION Fact& (N&)
      IF (N& < 2) THEN
         Fact& = 1
      ELSE
         Fact& = N& * Fact&(N& - 1)
      END IF
   END FUNCTION

This is another advantage of having 'local' variabled within subroutines
and functions: each invocation of the subroutine or function has its own
set of working variables.  When a subroutine or function is invoked, the
arguments and local variables are stored in a dynamic structure called the
'program stack'.  When a subroutine or function calls itself recursively,
each invovation puts another set of arguments and local variables on the
stack.  Note that any recursive subroutine or function MUST have at least
one return that is not recursive (like the 'Fact& = 1' above), or it will
keep calling itself until it 'blows' (exceeds the capacity of) the program
stack.

Have fun!
