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: 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 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) 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 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 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!