Writing Subroutines and Functions

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.

dfhrx006

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.

dfhrx010

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.

IMPORTANT NOTE

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.

dfhrx007

The following illustrates an external function named func2.

dfhrx011

When to Use Internal Versus External Subroutines or Functions

To determine whether to make a subroutine or function internal or external, you might consider factors, such as:

Passing Information

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.)

Passing Information by Using Variables

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.

Figure 29. Example of Passing Information in a Variable Using a Subroutine
/******************************* 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.

Figure 30. Example of Passing Information in a Variable Using a Function
/******************************* 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.

Figure 31. Example of a Problem Caused by Passing Information in a Variable Using a Subroutine
/******************************* 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.

Figure 32. Example of a Problem Caused by Passing Information in a Variable Using a Function
/******************************* 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:

Protecting Variables with the PROCEDURE Instruction

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.

Figure 33. Example of Subroutine Using the PROCEDURE Instruction
/******************************* 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

Figure 34. Example of Subroutine without the PROCEDURE Instruction
/******************************* 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.

Figure 35. Example of Function Using the PROCEDURE Instruction
/******************************* 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

Figure 36. Example of Function without the PROCEDURE Instruction
/******************************* 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
Exposing Variables with PROCEDURE EXPOSE

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.

Figure 37. Example Using PROCEDURE EXPOSE in 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.

Figure 38. Example Using PROCEDURE EXPOSE in a Function
/******************************* 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.

Passing Information by Using Arguments

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,...)
Using the ARG Instruction

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.

Figure 39. Example of Passing Arguments on the CALL Instruction
dfhrx009

The next example is the same except it uses ARG in a function instead of a subroutine.

Figure 40. Example of Passing Arguments on the Call to an Internal Routine
dfhrx012

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.

Using the ARG Built-in Function

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.

Receiving Information from a Subroutine or Function

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:

Exercise - Writing an Internal and an External Subroutine

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

Figure 41. Possible Solution (Main Program)
/***************************** 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

Figure 42. Possible Solution (Internal Subroutine Named CHECK)
/*************************** 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

Figure 43. Possible Solution (External Subroutine named THROW)
/******************************* 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

Exercise - Writing a Function

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

Figure 44. Possible Solution
/******************************* 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