12. Subroutines and functions The following program defines an internal function, and calls it with some data. "Internal" just means the function is written in the same file as the rest of the program. /* Define a function */ say "The results are:" square(3) square(5) square(9) exit square: /* function to square its argument */ parse arg in return in*in The output from this program should be: "The results are: 9 25 81" When Rexx finds the function call "square(3)", it searches the program for a label called "square". It finds the label on line 5 - the name followed by a colon. The interpreter executes the code which starts at that line, until it finds the instruction "return". While that code is being executed, the arguments to the function can be determined with "parse arg" in the same way as the arguments to a program. When the "return" instruction is reached, the value specified is evaluated and used as the value of "square(3)". The "exit" instruction in this program causes it to finish executing instead of running into the function. A function which takes multiple arguments may be defined, simply by separating the arguments with a comma. That is, like this: /* Define a function with three arguments */ say "The results are:" conditional(5,"Yes","No") conditional(10,"X","Y") exit conditional: /* if the first argument is less than 10 then return the second, else return the third. */ parse arg c,x,y if c<10 then return x else return y A subroutine is similar to a function, except that it need not give a value after the "return" instruction. It is called with the "call" instruction. /* Define a subroutine to print a string in a box, then call it */ call box "This is a sentence in a box" call box "Is this a question in a box?" exit box: /* Print the argument in a box */ parse arg text say "+--------------------------------+" say "|"centre(text,32)"|" /* centre the text in the box */ say "+--------------------------------+" return It is possible to call a function, even a built-in function, as if it were a subroutine. The result returned by the function is placed into the variable called "result". /* print the date, using the "call" instruction */ call date "N" say result If a function or subroutine does not need to use the variables which the caller is using, or if it uses variables which the caller does not need, then you can start the function with the "procedure" instruction. This clears all the existing variables away out of sight, and prepares for a new set of variables. This new set will be destroyed when the function finishes executing. The following program calculates the factorial of a number recursively: /* Calculate factorial x, that is, 1*2*3* ... *x */ parse pull x . say x"!="factorial(x) exit factorial: /* calculate the factorial of the argument */ procedure parse arg p if p<3 then return p else return factorial(p-1) * p The variable p which holds the argument to funtion factorial is unaffected during the calculation of factorial(p-1), because it is hidden by the "procedure" instruction. If the subroutine or function needs access to just a few variables, then you can use "procedure expose" followed by the list of variable names to hide away all except those few variables. You can write functions and subroutines which are not contained in the same Rexx program. In order to do this, write the function and save it into a file whose name will be recognised by the interpreter. This type of function is called an "external" function, as opposed to an "internal" function which can be found inside the currently running program. I If you want to call your function or subroutine using "call foobar" or I "foobar()", then you should save it in a file named "foobar.rexx" which I can be found in the current directory or in your path. O If you want to call your function or subroutine using "call foobar" or O "foobar()", then you should save it in a file named "foobar.cmd" which O can be found in the current directory or in your path. The "procedure" instruction is automatically executed before running your external function, and so it should not be placed at the start of the function. It is not possible for an external function to access any of its caller's variables, except by the passing of parameters. For returning from external functions, as well as the "return" instruction there is "exit". The "exit" instruction may be used to return any data to the caller of the function in the same way as "return", but "exit" can be used to return to the caller of the external function even when it is used inside an internal function (which is in turn in the external function). "exit" may be used to return from an ordinary Rexx program, as we have seen. In this case, a number may be supplied after "exit", which will be used as the exit code of the interpreter.