Rexx Programming/How to Rexx/subroutine
"Procedures" or "subroutines" are self-contained sections within the code that carry out specific tasks. They are small programs within the larger program. We can activate them within our code by using a CALL statement. A subroutine can signal that it has finished its task using a RETURN statement.
/* Call subroutines to make different animal noises. */ call BarkFiveTimes call MeowThreeTimes call MooEightTimes exit BarkFiveTimes: do 5 say "Woof!" end return MeowThreeTimes: do 3 say "Meow!" end return MooEightTimes: do 8 say "Moo!" end return
Parsing arguments
editIdeally, subroutines will be flexible enough to respond to various situations. We should be able to feed one or more pieces data into a subroutine to modify its behavior. These pieces of data are called "arguments", and you can include them within the CALL statement. They can be assigned to variables within the subroutine using a PARSE ARG statement.
/* Bark or meow as many times as you want. */ call Bark 5 call Meow 3 exit Bark: parse arg bark_count do bark_count say "Woof!" end return Meow: parse arg meow_count do meow_count say "Meow!" end return
Local variables
editSince it's annoying to need different variable names for the same purpose in different subroutines, we can make a function totally self-contained by using the PROCEDURE keyword. It will have its own variables that won't conflict with other variables elsewhere in the program, even if they have the same name.
call Bark 5 call Meow 3 exit Bark: procedure parse arg count do count say "Woof!" end return Meow: procedure parse arg count do count say "Meow!" end return
Code reuse
editAnytime you see the same kind of code emerging in different parts of your program, it's likely you have a single process that can be set aside in a procedure. That way, any mistakes can be corrected just once, in only one place.
/* Make any animal noise as many times as you want. */ call MakeNoise "Woof!", 5 call MakeNoise "Meow!", 3 call MakeNoise "Moo!", 8 exit MakeNoise: procedure parse arg noise, count do count say noise end return
Return values
editNot only can subroutines receive values from the part of the program that calls them, but they can send data back when they finish. The subroutine returns a piece of data by including it in the RETURN statement, after which it will be available in a variable called RESULT.
/* Use a subroutine to convert angle measures. */ say "Enter a number of degrees:" pull number call GetRadians number say number "degrees is the same as" result "radians." exit GetRadians: procedure parse arg degrees pi = 3.14159 return pi * degrees / 180
As a shortcut to calling the subroutine and accessing the result in two different statements, you can use the single-line notation using parentheses described in the function section of this book.
say "Enter a number of degrees:" pull number say number "degrees is the same as " GetRadians(number) "radians." exit
By the way, if you would like the arguments to be capitalized when they enter a subroutine, or if you don't care about letter case because the arguments are numbers, you leave out the PARSE keyword.
GetRadians: procedure arg degrees pi = 3.14159 return pi * degrees / 180
Exposed variables
editIf you want the procedure to be only partially self-contained, you can give it access to particular global variables using the EXPOSE keyword. This can be useful when you have some data that needs to be shared between different parts of the program.
/* Use the same value of pi to perform different circle computations. */ PI = 3.14159 call CircleArea 10 say "The area of a circle with radius 10 is" result"." call Circumference 50 say "The circumference of a circle of diameter 50 is" result"." exit CircleArea: procedure expose PI arg radius return PI * radius ** 2 Circumference: procedure expose PI arg diameter return PI * diameter
Recursion
editSubroutines can also call themselves and receive their own return values. The effect is similar to a loop and is useful if the subroutine needs to start over and try something again, perhaps with slightly different data, in order to complete its original task.
/* First 10 triangular numbers */ call ShowTriangulars 10 exit ShowTriangulars: procedure arg how_many if how_many > 0 then do one_less = how_many - 1 call ShowTriangulars one_less say Triangular(how_many) end return Triangular: procedure arg which? if which? > 1 then do previous = which? - 1 return Triangular(previous) + which? end return 1
If you expect a function to repeat itself hundreds or thousands of times, you are likely better off to code a loop inside it. A program can crash if the same function calls itself too many times before returning.