Subroutines
A subroutine is a collection of commands that allows you to perform a given task repeatedly, with minor variations, without actually duplicating the commands. You may also use subroutines from one program to perform the same task in other programs.
Defining Subroutines
A subroutine begins with the keyword subroutine followed by the name of the routine and any arguments, and ends with the keyword endsub. Any number of commands can appear in between. The simplest type of subroutine has the following form:
subroutine z_square
series x = z^2
endsub
where the keyword subroutine is followed only by the name of the routine. This subroutine has no arguments so that it will behave identically every time it is used. It forms the square of the existing series Z and stores it in the new series X.
You may use the
return command to force EViews to exit from the subroutine at any time. A common use of
return is to exit from the subroutine if an unanticipated error is detected. The following program exits the subroutine if Durbin’s
statistic (Greene, 2008, p. 646, or Davidson and MacKinnon, 1993, p. 360) for testing serial correlation with a lagged dependent variable cannot be computed:
subroutine durbin_h
equation eqn.ls cs c cs(-1) inc
scalar test=1-eqn.@regobs*eqn.@cov(2,2)
if test<=0 then
return
endif
scalar h=(1-eqn.@dw/2)*sqr(eqn.@regobs/test)
endsub
Subroutine with Arguments
The subroutines we have seen thus far have been written to work with a specific set of variables. More generally, subroutines can take arguments. Arguments allow you to change the behavior of the group of commands each time the subroutine is used. You may be familiar with the concept from other programming languages, but if now, you are probably familiar with similar concepts in mathematics. You can define a function, say
| (6.1) |
where
depends upon the argument
. The argument
is merely a place holder—it’s there to define the function and it does not really stand for anything. Then, if you want to evaluate the function at a particular numerical value, say 0.7839, you can write
. If you want to evaluate the function at a different value, say 0.50123, you merely write
. By defining the function, you save yourself from writing the full function expression every time you wish to evaluate it for a different value.
To define a subroutine with arguments, you start with the subroutine keyword, followed by the subroutine name and (with no space) the arguments separated by commas, enclosed in parentheses. Each argument is specified by listing a type of EViews object, followed by the name of the argument. For example:
subroutine power(series v, series y, scalar p)
v = y^p
endsub
This subroutine generalizes the example subroutine Z_SQUARE. Calling the subroutine POWER will fill the series given by the argument V with the power P of the series specified by the argument Y. So if you set V equal to X, Y equal to Z, and P equal to 2, you will get the equivalent of the subroutine Z_SQUARE above. See the discussion below on how to call subroutines.
When creating subroutines with scalar or string arguments, you will define your arguments using the scalar or the string types. Beyond that, you have a choice of whether you can to make the corresponding argument a (temporary) program variable or a (permanent) workfile object:
• To make the argument a program variable, you should use a program variable name (beginning with a “!” for a control variable and a “%” for a string variable). If you choose to use program variables, they should be referred to using the “!” or “%” name inside the subroutine.
• To make the argument a workfile object, you should use a standard EViews object name. The object should be referred to by the argument name inside the subroutine.
Obviously, you can mix the two approaches in the definition of any subroutine.
For example, the declaration
subroutine mysub(scalar !a, string %b)
uses program variable names, while
subroutine mysub(scalar a, string b)
uses object names. In the first case you should refer to “!A” and “%B” inside the subroutine; in the latter case, you should refer to the objects named “A” and “B”.
If you define your subroutine using program variables, the subroutine will operate on them as though they were any other program variable. The variables, which cannot go out-of-scope, should be referred to using the “!” or “%” argument name inside the subroutine.
If you define your subroutine using object names, the subroutine will operate on those variables as though they were scalar or string objects. The variables, which may be deleted and may go out-of-scope (if, for example, you change the workfile page), should be referred to using the argument names as though they were scalar or string objects.
(We discuss in detail related issues in
“Calling Subroutines”.)
You should note that EViews makes no distinction between input or output arguments in a subroutine. Each argument may be an input to the subroutine, an output from the subroutine, or both (or neither!). There is no way to declare that some arguments are inputs and some are outputs. There is no need to order the arguments in any particular order. However we find it much easier to read subroutine code when we stick to a convention, such as listing all output arguments prior to all input arguments (or vice versa).
Calling Subroutines
Once a subroutine is defined, you may execute the commands in the subroutine by using the call keyword. call should be followed by the name of the subroutine, and a list of any argument values you wish to use, enclosed in parentheses and separated by commas (with no space after the subroutine name). If the subroutine takes arguments, the arguments must be provided in the same order as in the declaration statement. Here is an example program file that calls subroutines:
include powers
load mywork
fetch z gdp
series x
series gdp2
series gdp3
call z_square
call power(gdp2,gdp,2)
call power(gdp3,gdp,3)
The call of the Z_SQUARE subroutine fills the series X with the value of Z squared. Next, the call to the POWER subroutine creates the series GDP2 which is GDP squared. The last call to POWER creates the series GDP3 as the cube of GDP.
When calling your subroutine, bear in mind that:
• When the subroutine argument is a scalar, the subroutine may be called with a scalar object, a control variable, a simple number (such as “10” or “15.3”), a matrix element (such as “mat1(1,2)”) or a scalar expression (such as “!y+25”).
• Subroutines with a string argument may be called with a string object, a string program variable, simple text (such as “hello”) or an element of an svector object.
• Subroutines that take matrix and vector arguments can be called with a matrix name, and if not modified by the subroutine, may also take a matrix expression.
• All other arguments must be passed to the subroutine with an object name referring to a single object of the correct type.
In
“Subroutine with Arguments” we described how you can
define subroutines that use either program variables or objects for scalar or string arguments. However you define your subroutine, you may call the subroutine using either program variables or objects—you are not required to match the calling arguments with the subroutine definition. Suppose, for example, that you define your subroutine as
subroutine mysub(scalar a, string b)
Then for scalar and string objects F and G, and program variables !X and %Y,
scalar f = 3
string g = "hello"
!x = 2
%y = "goodbye"
you may call the subroutine using any of the following commands:
call mysub(!x, %y)
call mysub(!x, g)
call mysub(f, %y)
call mysub(f, g)
Note that changes to the scalars A and B inside the subroutine will change the corresponding program variable or object that you pass into the routine.
Similarly, you may define
subroutine mysub(scalar !a, string !b)
and use the same four call statements to execute the subroutine commands.
However the subroutine is called, bear in mind that behavior inside the subroutine is dependent on whether the subroutine declaration is in terms of program variables or objects, not on the variable type that is passed into the subroutine.
Subroutine Placement
Subroutine definitions may be placed anywhere throughout your program. However, for clarity, we recommend grouping subroutine definitions together either at the start or at the end of your program. The subroutines will not be executed until they are executed by the program using a call statement. For example:
subroutine z_square
series x=z^2
endsub
load mywork
fetch z
call z_square
Execution of this program begins with the load statement; the subroutine definition is skipped and is executed only at the last line when it is “called.”
Subroutine definitions must not overlap—after the subroutine keyword, there should be an endsub before the next subroutine declaration. Subroutines may call each other, or even call themselves.
Alternatively, you may place frequently used subroutines in a separate program file and use an include statement to insert them at the beginning of your program. If, for example, you put the subroutine lines in the file “Powers.PRG”, then you may put the line:
include powers
at the top of any other program that needs to call Z_SQUARE or POWER. You can use the subroutines in these programs as though they were built-in parts of the EViews programming language.
Global and Local Variables
Subroutines work with variables and objects that are either global or local.
Global variables refer either to objects which exist in the workfile when the subroutine is called, and to objects that are created in the workfile by a subroutine. Global variables remain in the workfile when the subroutine finishes.
A local variable is one that is defined within the subroutine. Local variables are deleted from the workfile once a subroutine finishes. The program that calls the subroutine will not be able to use a local variable since the local variable disappears once the subroutine finishes and the original program continues.
Global Subroutines
By default, subroutines in EViews are global. Global subroutine may refer to any global object that exists in the workfile at the time the subroutine is called. Thus, if Z is a series in the workfile, the subroutine may refer to and, if desired, alter the series Z. Similarly, if Y is a global matrix that has been created by another subroutine, the current subroutine may use the matrix Y.
The rules for variables in global subroutines are:
• All objects created by a global subroutine are global and will remain in the workfile when the subroutine finishes.
• Global objects may be used and updated directly from within the subroutine. If, however, a global object has the same name as an argument in a subroutine, the variable name will refer to the argument and not to the global variable.
• The global objects corresponding to arguments may be used and updated by referring to the arguments.
Here is a simple program that calls a global subroutine:
subroutine z_square
series x = z^2
endsub
load mywork
fetch z
call z_square
Z_SQUARE is a global subroutine which has access to the global series Z. The new global series X contains the square of the series Z. Both X and Z remain in the workfile when Z_SQUARE is finished.
If one of the arguments of the subroutine has the same name as a global variable, the argument name takes precedence so that any reference to the name in the subroutine will refer to the argument, not to the global variable. For example:
subroutine sqseries(series z, string sername)
series {sername} = z^2
endsub
load mywork
fetch z
fetch y
call sqseries(y, "y2")
In this example, there is a series Z in the original workfile and Z is also an argument of the subroutine. Calling SQSERIES with the argument set to Y tells EViews to use the series passed-in via the argument Z instead of the global Z series. On completion of the routine, the new series Y2 will contain the square of the series Y, not the square of the series Z. Since keeping track of variables can become confusing when subroutine arguments take the same name as existing workfile objects, we encourage you to name subroutine arguments as clearly and distinctly as possible.
Global subroutines may call global subroutines. You should make certain to pass along required arguments when you call a subroutine from within a subroutine. For example,
subroutine wgtols(series y, series wt)
equation eq1
call ols(eq1, y)
equation eq2
series temp = y/sqr(wt)
call ols(eq2, temp)
delete temp
endsub
subroutine ols(equation eq, series y)
eq.ls y c y(-1) y(-1)^2 y(-1)^3
endsub
can be run by the program:
load mywork
fetch cpi
fetch cs
call wgtols(cs,cpi)
In this example, the subroutine WGTOLS must explicitly pass along arguments to the subroutine OLS so that it uses the correct series and equation objects.
You cannot use a subroutine to change the object type of a global variable. Suppose that we wish to declare new matrices X and Y by using a subroutine NEWXY. In this example, the declaration of matrix X generates an error since X exists and is a series, but the declaration of the matrix Y works (assuming there is no Y in the workfile MYWORK, or that Y exists and is already a matrix):
subroutine newxy
matrix(2,2) x = 0
matrix(2,2) y = 0
endsub
load mywork
series x
call newxy
If you call this subroutine, EViews will return an error indicating that the global series X already exists and is of a different type than a matrix.
Local Subroutines
If you include the word local in the definition of the subroutine, you create a local subroutine. Local subroutines are most useful when you wish to write a subroutine which creates many temporary objects that you do not want to keep.
The rules for variables in local subroutines are:
• All objects created by a local subroutine will be local and will be removed from the workfile upon exit from the subroutine.
• The global objects corresponding to subroutine arguments may be used and updated in the subroutine by referring to the arguments.
• You may not use or update global objects that do not correspond to subroutine arguments.
There is one exception to the general inaccessibility of non-argument global variables in local subroutines. When a global group is passed as an argument to a local subroutine, all of the series in the group are accessible to the local routine.
The last two rules deserve a bit of elaboration. In general, local subroutines do not have access to any global variables unless those variables are associated with arguments passed into the subroutine. Thus, if there is a series X in the workfile, a local subroutine will not be allowed to use or modify X unless it is passed into the subroutine using a series argument. Conversely, if X is passed into the subroutine, it may be modified.
Since locally created objects will vanish upon completion of the subroutine, to save results from a local subroutine, you have to include them as arguments. For example, consider the subroutine:
subroutine local ols_local(series y, series res, scalar ssr)
equation temp_eq.ls y c y(-1) y(-1)^2 y(-1)^3
temp_eq.makeresid res
ssr = temp_eq.@ssr
scalar se = ssr/temp_eq.@df
endsub
This local subroutine takes the series Y as input and modifies the argument series RES and argument scalar SSR as output. Note that since Y, RES, and SSR are the only arguments of this local subroutine, the only global variables that may be used or modified are those associated with these arguments.The equation object TEMP_EQ and the scalar SE are local to the subroutine and will vanish when the subroutine finishes.
Here is an example program that calls this local subroutine:
load mywork
fetch hsf
equation eq1.ls hsf c hsf(-1)
eq1.makeresid rres
scalar rssr = eq1.@ssr
series ures
scalar ussr
call ols_local(hsf, ures, ussr)
Note that we first declare the series URES and scalar USSR before calling the local subroutine. These objects are global since they are declared outside the local subroutine. Since we call the local subroutine by passing these global objects as arguments, the subroutine can use and update these global variables.
Object commands that require access to global variables should be used with care in local subroutines since that the lack of access to global variables can create problems for views or procs of objects passed into the routine. For example, a subroutine of the form:
subroutine local bg(equation eq)
eq.hettest z c
endsub
will fail because the hettest equation proc requires access to the original variables in the equation and the global variable Z, and these series are not available since they are not passed in as arguments to the subroutine.
Care should also be taken when using samples and local subroutines. If the workfile sample is based upon a series in the workfile (for example “smpl @all if x>0”), most procedures inside the local subroutine will fail unless all of the series used in the sample are passed into the subroutine.
Local Samples
Local samples in subroutines allow you to indicate that changes to the workfile sample are temporary, with the original sample restored when you exit the routine. This feature is useful when designing subroutines which require working on a subset of observations in the original sample.
You may, in a subroutine, use the local smpl statement to indicate that subsequent changes to the sample are temporary, and should be undone when exiting the subroutine. The command
local smpl
makes a copy of the existing sample specification. You may then change the sample as many times as desired using the smpl statement, and the original sample specification will be reapplied when existing from the subroutine.
You may use the global smpl statement to indicate that subsequent smpl statements will result in permanent changes to the workfile sample. Thus, the commands:
global smpl
smpl 5 100
in a subroutine permanently change the sample.
For example, consider the following program snippet which illustrates the behavior of local and global samples:
workfile temp u 100
call foo
subroutine foo
smpl 2 100
local smpl
smpl 10 100
endsub
Here, we create a workfile with 100 observations and an initial workfile sample of “1 100”, then call the subroutine FOO. Inside FOO, the first smpl statement changes the workfile sample to “2 100”. We then issue the local smpl statement which backs up the existing sample and identifies subsequent sample changes as local. The subsequent change to the “10 100” sample is local so that when the subroutine exits, the sample is reset to “2 100”.
If instead we define FOO to be
subroutine foo
smpl 2 100
local smpl
smpl 10 100
global smpl
smpl 5 100
local smpl
smpl 50 100
endsub
As before, first smpl statement changes the workfile sample to “2 100” and the local smpl statement and following smpl statement set the local sample to “10 100”. The global smpl indicates that subsequent sample statements will once again be global so the next line permanently changes the workfile sample to “5 100”. Note that the last local smpl and subsequent smpl statement change the local sample only. When we exit the subroutine the sample will be set to the last global sample of “5 100”.