RATS 10.1
RATS 10.1

Programming Tools / Procedures and Functions /

Writing Your Own Procedures and Functions

Home Page

← Previous Next →

The instructions that make up the subprogram must be in the following order:

 

PROCEDURE or FUNCTION statement

 TYPE and OPTION statements

 other RATS instructions

END

 

The first line provides the subprogram name and lists the names of the formal parameters. TYPE sets the variable types of the parameters and the function, while OPTION instructions define any options for a procedure. DECLARE and LOCAL statements may be scattered among the other instructions, but it’s usually a good idea to keep them together before the executable instructions.

 

Learning How to Write Procedures

Start small!! If the first procedure you try to write uses 100 RATS instructions to do the latest wrinkle in statistical theory, you are likely to be working at it for a very long time. Begin with a more modest (or even trivial) operation and work up from there. We would suggest that you start with parameters only, then add local variables and finally options (if they make sense for your application). Get it to work correctly with proper input before putting in checks for garbage input.

 

Learn from our procedures!! We have provided many procedures which use the most advanced techniques that RATS has to offer.

 

Parameters

Parameters allow you to pass information stored in variables (data series, scalar variables, etc.) from the main program to a subprogram. You list the parameters on the PROCEDURE or FUNCTION statement itself following the subprogram name, with the parameters separated by blanks. We refer to these as formal parameters. You use TYPE statements to indicate what type of information they provide. You pass information to these formal parameters by specifying a matching set of actual parameters on the EXECUTE instruction or function reference. Within the subprogram, you manipulate the data using the formal parameter names. If you pass a parameter by address (see next segment), you can either use or set it within the procedure. If, however, you pass a parameter by value, you may only use it within the procedure.

 

By default, formal parameters of a PROCEDURE are all type INTEGER, passed by value, and those of a FUNCTION are REAL, again passed by value. The FUNCTION name itself is REAL by default. You will need to use one or more TYPE statements if you want them to take different types.

 

You should keep the number of parameters to a minimum. Options tend to be much easier to use. Parameters have to be done in a particular order, so you should avoid anything beyond the typical  series start end result.

 

Passing Parameters: By Value vs. By Address

Parameters can be passed to a procedure in one of two ways: by address, or by value.

Passing by address means that the actual memory address of a variable is passed to the procedure. If you change the value of the formal parameter inside the procedure, you will change the value of the actual parameter.

Passing by value means that the only information made available to the procedure is the value of the actual parameter. The procedure is not allowed to manipulate the value of the formal (or actual) parameter. In fact, you will get a syntax error if you try to reset the value.

You should pass a parameter argument by address only if it is being set by the procedure. Anything that is only an input to the procedure should be passed by value.

 

You distinguish between the two by prefixing the parameter name with * on a TYPE statement if you want it called by address. Without the *, it is called by value.

 

proc forecast depvar start end forecast

type series  depvar *forecast

type integer start  end

 

Parameters DEPVAR and FORECAST are both type SERIES. DEPVAR, however, is called by value, and FORECAST by address. START and END are INTEGERS, passed by value. So, DEPVAR, START, and END will be inputs to the procedure and FORECAST will be the output that is computed and returned to the main program.

 

 

function sum num1 num2

type integer sum num1 num2

compute sum = num1+num2

end

*

compute a=5, b=10

? a "+" b "=" sum(a,b)

 

This computes the sum of two integer values, and returns the result as an integer. NUM1 and NUM2 are passed by value, so the function can use these variables, but it cannot change their value. The function name itself is always considered to be passed by address (there wouldn’t be much point otherwise), so you don’t need a * on it.

 

It’s a good idea to include a TYPE for every parameter. If you don’t, the default for a PROCEDURE parameter is INTEGER and for a FUNCTION is REAL. (This reflects the most common uses for each).

 

Local Variables

The variables in a RATS program are global unless you state otherwise—that is, you can use them within any part of the program, including procedures. There can be only one global variable with any one name.

 

A local variable (defined by LOCAL) can only be used in a subprogram, and only within the subprogram which defines it. RATS keeps global and local variables completely separate, so a local variable can have the same name as a global one. If a name conflict arises, the local variable takes precedence.

 

If you plan to use a subprogram in many different programs, you should write it using only parameters, local variables, and variables defined internally by RATS. That way there can be no possibility of conflict with the variable names in the main program. If you want some variables computed within a procedure to be accessible to the user later (the way that %RSS and %BETA are for an instruction like LINREG), it is a good idea to use a special prefix (such as %%) on the variable names, to avoid the possibility of conflict with the user’s own variable names, or with RATS reserved names in future releases.

 

LOCAL takes the same form as DECLARE.

 

procedure bayestst series start end

type series   series

type integer  start end

local series  sdiff strend

local integer startl endl

local real    teststat margalph alphtest

 

The procedure @BAYESTST has several local variables. Note particularly the use of local series. If you use a series locally, it is very important to declare it as such.

 

Options

The OPTION statement defines options for the procedure. (They can’t be used with a function). When you call the procedure, you can choose options in a field immediately after the procedure name, much like options on regular RATS instructions. You can also define default values or settings for procedure options. Like parameters, options can be passed by value or by address. To have it passed by address, prefix the formal parameter name with *.

 

See the description for OPTION for details—we just give some quick information here. For the three types (switch, choice and value), use

 

option  switch     option name   Default (1 for ON, 0 for OFF)

option  choice     option name   Default number   List of Choices

option  data type   option name   Default value

Option names are local to the procedure.

You must use the full name for option name within the procedure.

When you invoke the procedure using EXECUTE or @procedure, only the first three characters of the option name are significant (just like standard RATS options). Make sure you do not use conflicting option names. For instance, RESIDUALS and RESTRICT both abbreviate to RES. RATS will give an error if you use two options with conflicting names.

Do not use a name which begins with the letters NO. This is reserved for turning off switch options.

 

Passing Information to RATS Instructions

RATS allows you to pass information to instruction options in a more flexible fashion than was described in the Introduction. With all types of options (including switch and choice), you can use the syntax

 

 instruction(option=expression or variable)

 

procedure nonsense

option switch print 1

option symmetric *vcvmat

vcv(print=print,matrix=vcvmat)

end

 

In this example, the PRINT switch is on by default, so the PRINT option on VCV will also be on by default. If, however, you use the option NOPRINT when you execute NONSENSE, the PRINT variable will be “off” (value 0) which will turn off the PRINT option on VCV.

 

The VCVMAT option has no default. If you don’t use the option when you execute the procedure, the MATRIX option on VCV will behave as if you didn’t use it at all.

 

Supplementary Cards

RATS allows you some flexibility in dealing with supplementary cards for RATS instructions within the procedure. You can either:

code them immediately after the instruction inside the procedure which requires them (if you know what the values on the card will be), or

put them after the EXECUTE instruction which invokes the procedure. If an instruction with an omitted supplementary card is within a loop, EXECUTE must be followed by one supplementary card for each pass through the loop.

The latter method allows you to act as if the supplementary cards are for the PROCEDURE rather than for instruction within it which requires them. When a single instruction requires several supplementary cards, you must treat them all in the same fashion—you cannot include three in the procedure and leave out the fourth.

 

If you need a list of regressors, the best way to get them is by using an EQUATION instruction applied to a local equation. For instance:

 

local equation xvar

equation xvar *

 

This will define the equation using the regressor list that follows the line executing the procedure. This was taken from the @DISAGGREGATE procedure, so

 

@disaggregate(type=linear) loggdp

# constant  logip logsales

 

will define XVAR (within the procedure) as an equation with CONSTANT, LOGIP and LOGSALES as its explanatory variables. You can then use the %EQNxxxx functions to work with this. For instance, %EQNSIZE(XVAR) will be the number of variables in the list, %EQNTABLE(XVAR) the table of series/lag combinations and %EQNXVECTOR(XVAR,T) will be the vector ||1,LOGIP(T),LOGSALES(T)||.

 

If you need some other type of list of series (where lag notation isn’t permitted), bring it in as an VECTOR[INTEGER] array using ENTER(VARYING) and use that array when you need the list. For instance, this is taken from the @JOHMLE procedure:

 

local vect[int] v

enter(varying) v

 

You can use %SIZE(V) to see how many elements there are in that.

 

The %DEFINED Function

You can use the function %DEFINED(option/parameter name) to determine whether an option or parameter was given a value when the procedure was executed. It returns 1 if it was defined and 0 otherwise. If an option has a default value, it will always be defined, since it will, at minimum, get the default value. We use %DEFINED extensively in our procedures to ensure the user executed the procedure properly.

 

The INQUIRE Instruction

INQUIRE is very handy when you want to write a procedure which (perhaps optionally) determines the range itself. We use it in most of our procedures. For instance,

 

procedure stockwat series start end

type series series

type integer start end

option integer arcorr 1

local integer startl startm endm

 

inquire(series=series) startm<<start endm<<end

inquire(series=series) startl

if .not.%defined(start)

   compute startm=startl+arcorr+3

 

The first INQUIRE sets STARTM to the value of the parameter START if the user provides one, otherwise it will be the first defined entry of SERIES. ENDM is similar. The second INQUIRE just determines the first defined entry of SERIES. If there was no value for START, the COMPUTE sets STARTM to begin ARCORR+3 entries into SERIES.

 

The FIXED Statement—Creating Lookup Tables

FIXED can be used in a subprogram to set up a lookup table for (for instance) critical values. It creates an array (or array of arrays)  whose dimensions and content are known constants. You can’t use expressions in the dimensions or the values.

 

FIXED can set up only one array per statement, which is followed by its values. It can be placed anywhere within the subprogram, though it generally makes sense to put it near the top, typically between the other declaration statements and the first executable instructions. The array that it creates is local to the subprogram. The following creates a lookup table with three branches, each of which is \(2 \times 6\). You would access a value of this with CL(branch needed)(row needed,column needed).

 

fixed vect[rect] cl(3)(2,6)

 -1.6156   -0.1810    0.0000    0.0000    0.0000    0.0000

 -1.9393   -0.3980    0.0000    0.0000    0.0000    0.0000

 

 -1.9480  -17.8390  104.0860    0.8020    5.5580  -18.3320

 -1.6240  -19.8880  155.2310    0.7090    5.4800  -16.0550

 

 -2.8380  -20.3280  124.1910    1.2670   10.5300  -24.6000

 -2.5500  -20.1660  155.2150    1.1330    9.8080  -20.3130

 


Copyright © 2025 Thomas A. Doan