A subroutine is a series of instructions that a program calls to perform a specific task. The instruction that calls the subroutine is the CALL instruction. You can use the CALL instruction several times in a program to call the same subroutine.
When the subroutine ends, it can return control to the instruction that directly follows the subroutine call. The instruction that returns control is the RETURN instruction.
A function is a series of instructions that a program calls to perform a specific task and return a value. As Using Functions describes, a function can be built-in or user-written. Call a user-written function the same way as a built-in function: specify the function name immediately followed by parentheses that can contain arguments. There can be no blanks between the function name and the left parenthesis. The parentheses can contain up to 20 arguments or no arguments at all.
function(argument1, argument2,...)
or
function()
A function requires a return value because the function call generally appears in an expression.
z = function(arguments1, argument2,...)
When the function ends, it can use the RETURN instruction to send back a value to replace the function call.
Both subroutines and functions can be internal (designated by a label) or external (designated by the subroutine or function in the REXX File System/VSE Librarian sublibrary member name). The two preceding examples illustrate an internal subroutine named sub1 and an internal function named func1.
Because internal subroutines and functions generally appear after the main part of the program, when you have an internal subroutine or function, it is important to end the main part of the program with the EXIT instruction.
The following illustrates an external subroutine named sub2.
The following illustrates an external function named func2.
To determine whether to make a subroutine or function internal or external, you might consider factors, such as:
A program and its internal subroutine or function can share the same variables. Therefore, you can use commonly shared variables to pass information between caller and internal subroutine or function. You can also use arguments to pass information to and from an internal subroutine or an internal function. External subroutines, however, cannot share variables with the caller. To pass information to them, you need to use arguments or some other external way, such as the data stack. (Remember: An internal function does not need to pass arguments within the parentheses that follow the function call. However, all functions, both internal and external, must return a value.)
When a program and its internal subroutine or function share the same variables, the value of a variable is what was last assigned. This is regardless of whether the assignment was in the main part of the program or in the subroutine or function.
The following example shows passing information to a subroutine. The variables number1, number2, and answer are shared. The value of answer is assigned in the subroutine and used in the main part of the program.
/******************************* REXX ********************************/
/* This program receives a calculated value from an internal */
/* subroutine and uses that value in a SAY instruction. */
/*********************************************************************/
number1 = 5
number2 = 10
CALL subroutine
SAY answer /* Produces 15 */
EXIT
subroutine:
answer = number1 + number2
RETURN
The next example is the same, except it passes information to a function rather than a subroutine. The subroutine includes the variable answer on the RETURN instruction. The language processor replaces the function call with the value in answer.
/******************************* REXX ********************************/
/* This program receives a calculated value from an internal */
/* function and uses SAY to produce that value. */
/*********************************************************************/
number1 = 5
number2 = 10
SAY add() /* Produces 15 */
SAY answer /* Also produces 15 */
EXIT
add:
answer = number1 + number2
RETURN answer
Using the same variables in a program and its internal subroutine or function can sometimes create problems. In the next example, the main part of the program and the subroutine use the same control variable, i, for their DO loops. As a result, the DO loop runs only once in the main program because the subroutine returns to the main program with i = 6.
/******************************* REXX ********************************/
/* NOTE: This program contains an error. */
/* It uses a DO loop to call an internal subroutine, and the */
/* subroutine uses a DO loop with the same control variable as the */
/* main program. The DO loop in the main program runs only once. */
/*********************************************************************/
number1 = 5
number2 = 10
DO i = 1 TO 5
CALL subroutine
SAY answer /* Produces 105 */
END
EXIT
subroutine:
DO i = 1 TO 5
answer = number1 + number2
number1 = number2
number2 = answer
END
RETURN
The next example is the same, except it passes information using a function instead of a subroutine.
/******************************* REXX ********************************/
/* NOTE: This program contains an error. */
/* It uses a DO loop to call an internal function, and the */
/* function uses a DO loop with the same control variable as the */
/* main program. The DO loop in the main program runs only once. */
/*********************************************************************/
number1 = 5
number2 = 10
DO i = 1 TO 5
SAY add() /* Produces 105 */
END
EXIT
add:
DO i = 1 TO 5
answer = number1 + number2
number1 = number2
number2 = answer
END
RETURN answer
To avoid this kind of problem in an internal subroutine or function, you can use:
When you use the PROCEDURE instruction immediately after the subroutine or function label, all variables in the subroutine or function become local to the subroutine or function; they are shielded from the main part of the program. You can also use the PROCEDURE EXPOSE instruction to protect all but a few specified variables.
The following examples show how results differ when a subroutine or function uses or does not use PROCEDURE.
/******************************* REXX ********************************/
/* This program uses a PROCEDURE instruction to protect the */
/* variables within its subroutine. */
/*********************************************************************/
number1 = 10
CALL subroutine
SAY number1 number2 /* Produces 10 NUMBER2 */
EXIT
subroutine: PROCEDURE
number1 = 7
number2 = 5
RETURN
/******************************* REXX ********************************/
/* This program does not use a PROCEDURE instruction to protect the */
/* variables within its subroutine. */
/*********************************************************************/
number1 = 10
CALL subroutine
SAY number1 number2 /* Produces 7 5 */
EXIT
subroutine:
number1 = 7
number2 = 5
RETURN
The next two examples are the same, except they use functions rather than subroutines.
/******************************* REXX ********************************/
/* This program uses a PROCEDURE instruction to protect the */
/* variables within its function. */
/*********************************************************************/
number1 = 10
SAY pass() number2 /* Produces 7 NUMBER2 */
EXIT
pass: PROCEDURE
number1 = 7
number2 = 5
RETURN number1
/******************************* REXX ********************************/
/* This program does not use a PROCEDURE instruction to protect the */
/* variables within its function. */
/*********************************************************************/
number1 = 10
SAY pass() number2 /* Produces 7 5 */
EXIT
pass:
number1 = 7
number2 = 5
RETURN number1
To protect all but specific variables, use the EXPOSE option with the PROCEDURE instruction, followed by the variables that are to remain exposed to the subroutine or function.
The next example uses PROCEDURE EXPOSE in a subroutine.
/******************************* REXX ********************************/
/* This program uses a PROCEDURE instruction with the EXPOSE option */
/* to expose one variable, number1, in its subroutine. The other */
/* variable, number2, is set to null and the SAY instruction */
/* produces this name in uppercase. */
/*********************************************************************/
number1 = 10
CALL subroutine
SAY number1 number2 /* produces 7 NUMBER2 */
EXIT
subroutine: PROCEDURE EXPOSE number1
number1 = 7
number2 = 5
RETURN
The next example is the same except PROCEDURE EXPOSE is in a function instead of a subroutine.
/******************************* REXX ********************************/
/* This program uses a PROCEDURE instruction with the EXPOSE option */
/* to expose one variable, number1, in its function. */
/*********************************************************************/
number1 = 10
SAY pass() number1 /* Produces 5 7 */
EXIT
pass: PROCEDURE EXPOSE number1
number1 = 7
number2 = 5
RETURN number2
For more information about the PROCEDURE instruction, see section PROCEDURE.
A way to pass information to either internal or external subroutines or functions is through arguments. When calling a subroutine, you can pass up to 20 arguments separated by commas on the CALL instruction as follows:
CALL subroutine_name argument1, argument2, argument3,...
In a function call, you can pass up to 20 arguments separated by commas.
function(argument1,argument2,argument3,...)
A subroutine or function can receive the arguments with the ARG instruction. In the ARG instruction, commas also separate arguments.
ARG arg1, arg2, arg3, ...
The names of the arguments that are passed do not have to be the same as those on the ARG instruction because information is passed by position rather than by argument name. The first argument sent is the first argument received and so forth. You can also set up a template in the CALL instruction or function call. The language processor then uses this template in the corresponding ARG instruction. For information about parsing with templates, see section Parsing Data.
In the following example, the main routine sends information to a subroutine that computes the perimeter of a rectangle. The subroutine returns a value in the variable perim by specifying the value in the RETURN instruction. The main program receives the value in the special variable RESULT.
The next example is the same except it uses ARG in a function instead of a subroutine.
In the two preceding examples, notice the positional relationships between long and length, and wide and width. Also notice how information is received from variable perim. Both programs include perim on a RETURN instruction. For the program with a subroutine, the language processor assigns the value in perim to the special variable RESULT. For the program using a function, the language processor replaces the function call perimeter(long,wide) with the value in perim.
Another way for a subroutine or function to receive arguments is with the ARG built-in function. This function returns the value of a particular argument. A number represents the argument position.
For instance, in the previous example, instead of the ARG instruction:
ARG length, width
you can use the ARG function as follows:
length = ARG(1) /* puts the first argument into length */
width = ARG(2) /* puts the second argument into width */
For more information about the ARG function see section ARG.
Although a subroutine or function can receive up to 20 arguments, it can specify only one expression on the RETURN instruction. That expression can be:
RETURN 55
RETURN value1 value2 value3
RETURN 'Work complete.'
RETURN 5 * number
Write a program that plays a simulated coin toss game and produces the accumulated scores.
There should be four possible inputs:
Write an internal subroutine without arguments to check for valid input. Send valid input to an external subroutine that uses the RANDOM built-in function to generate random outcomes. Assume HEADS = 0 and TAILS = 1, and use RANDOM as follows:
RANDOM(0,1)
Compare the valid input with the value from RANDOM. If they are the same, the user wins one point; if they are different, the computer wins one point. Return the result to the main program where results are tallied.
ANSWER
/***************************** REXX ********************************/
/* This program plays a simulated coin toss game. */
/* The input can be heads, tails, or null ("") to quit the game. */
/* First an internal subroutine checks input for validity. */
/* An external subroutine uses the RANDOM built-in function to */
/* obtain a simulation of a throw of dice and compares the user */
/* input to the random outcome. The main program receives */
/* notification of who won the round. It maintains and produces */
/* scores after each round. */
/*******************************************************************/
PULL flip /* Gets "HEADS", "TAILS", or "" */
/* from input stream. */
computer = 0; user = 0 /* Initializes scores to zero */
CALL check /* Calls internal subroutine, check */
DO FOREVER
CALL throw /* Calls external subroutine, throw */
IF RESULT = 'machine' THEN /* The computer won */
computer = computer + 1 /* Increase the computer score */
ELSE /* The user won */
user = user + 1 /* Increase the user score */
SAY 'Computer score = ' computer ' Your score = ' user
PULL flip
CALL check /* Call internal subroutine, check */
END
EXIT
/*************************** REXX ************************************/
/* This internal subroutine checks for valid input of "HEADS", */
/* "TAILS", or "" (to quit). If the input is anything else, the */
/* subroutine says the input is not valid and gets the next input. */
/* The subroutine keeps repeating until the input is valid. */
/* Commonly used variables return information to the main program */
/*********************************************************************/
check:
DO UNTIL outcome = 'correct'
SELECT
WHEN flip = 'HEADS' THEN
outcome = 'correct'
WHEN flip = 'TAILS' THEN
outcome = 'correct'
WHEN flip = '' THEN
EXIT
OTHERWISE
outcome = 'incorrect'
PULL flip
END
END
RETURN
/******************************* REXX ********************************/
/* This external subroutine receives the valid input, analyzes it, */
/* gets a random "flip" from the computer, and compares the two. */
/* If they are the same, the user wins. If they are different, */
/* the computer wins. The routine returns the outcome to the */
/* calling program. */
/*********************************************************************/
throw:
ARG input
IF input = 'HEADS' THEN
userthrow = 0 /* heads = 0 */
ELSE
userthrow = 1 /* tails = 1 */
compthrow = RANDOM(0,1) /* choose a random number */
/* between 0 and 1 */
IF compthrow = userthrow THEN
outcome = 'human' /* user chose correctly */
ELSE
outcome = 'machine' /* user chose incorrectly */
RETURN outcome
Write a function named AVG that receives a list of numbers separated by blanks and computes their average. The final answer can be a decimal number. To call this function, you would use:
AVG(number1 number2 number3...)
Use the WORDS (see section WORDS) and WORD (see section WORD) built-in functions.
ANSWER
/******************************* REXX ********************************/
/* This function receives a list of numbers, adds them, computes */
/* their average, and returns the average to the calling program. */
/*********************************************************************/
ARG numlist /* receive the numbers in a single variable */
sum = 0 /* initialize sum to zero */
DO n = 1 TO WORDS(numlist) /* Repeat for as many times as there */
/* are numbers */
number = WORD(numlist,n) /* Word #n goes to number */
sum = sum + number /* Sum increases by number */
END
average = sum / WORDS(numlist) /* Compute the average */
RETURN average