What is the scope of variables during function evaluation?
- All variables created during function evaluation will be local variables which exist until the function completes and returns its output value.
- When function A calls function B, a different set of local variables will be created, some of which may have the same names as those in function A. To keep track of the different variables and their values, TIBCO Spotfire S+ uses "frames".
What is a frame?
- A frame is best thought of as a set of temporarily stored S+ objects.
- Unlike objects in directories on the 'search()' list, which are files stored on a hard disk, the contents of a frame are stored in memory.
- A frame can exist, at the longest, for the duration of the S+ session.
- Objects stored in frames can be accessed more quickly than objects stored in directories, because they reside in memory, rather than on the hard drive.
How are frames organized?
- The _session_ frame (frame 0) exists for the duration of the S+ session. It holds objects that can reasonably be expected to start over from default values, such as '.Options'.
- The _expression_ frame (frame 1) holds objects throughout the evaluation of a typed expression. Objects in the expression frame disappear after the complete evaluation of the top-level expression.
- During the evaluation of an expression, higher numbered fames (frame n, where n>=2) are created temporarily.
When S+ searches for objects, it looks for them in each of the following locations, in the order shown:
- the local frame (frame n)
- the expression frame (frame 1)
- the session frame (frame 0)
- the directories in the 'search()' path
What this means:
- This means that if you are in frame 7, and want to access something in frame 6 (for example, during the execution of a function), some special provisions are necessary.
- A key point to remember is that it doesn't matter how deeply nested your calls become: the search path always jumps from the current evaluation frame to frame 1, bypassing all of the evaluation frames in between.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Resources for further information on S+ evaluation frames:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Chapter 2 of the TIBCO Spotfire S+ 8.1 Programmer's Guide for Windows has the most complete treatment of this subject.
You can also find information related to the use of S+ frames in the on-line help files that are displayed when you type the following expressions at the S+ command-line prompt:
?frame.attributes
?assign
?new.frame
?eval
?move.frame
?syntax
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Q&A which illustrates the discussion above on frames:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Question:
~~~~~~~~~
"I wrote the following simple function to fit an 'lm()' model and plot the fit. Why do I get an error, and how do I get this to work?"
my.lm.func <- function(my.formula, my.data)
{
fit <- lm(my.formula, my.data)
old.par <- par(mfrow = c(3, 2))
on.exit(par <- old.par)
plot(fit)
fit
}
Output of a call to this function:
> my.lm.func(Mileage~Weight,fuel.frame)
Error: Object "my.formula" not found
Dumped
Answer:
~~~~~~~
The problem has to do with where S+ looks for objects. As "my.formula" is not one of the arguments to 'plot()' and is not in any directories in the search list, it cannot be found by 'plot()'. Basically, 'plot()' looks at the "call" component of 'fit' when constructing the Cook's distance plot, determines that the name of the formula used to fit the model is "my.formula", doesn't see "my.formula" as an argument to 'plot()' or created within 'plot, and then goes looking down the search list for "my.formula".
Another place S+ looks for objects is in what is called the "expression frame". One solution to this problem is to place a copy of "my.formula" in the expression frame:
my.lm.func2 <- function(my.formula, my.data)
{
assign("my.data", my.data, frame = 1)
assign("my.formula", my.formula, frame = 1)
fit <- lm(my.formula, my.data)
old.par <- par(mfrow = c(3, 2))
on.exit(par(old.par)
plot(fit)
fit
}
Output of a call to this function:
> my.lm.func2(Mileage~Weight,fuel.frame)
Call:
lm(formula = my.formula, data = my.data)
Coefficients:
(Intercept) Weight
48.34935 -0.008192823
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Another discussion on the subject of frames:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In S, objects defined inside a function are not visible to functions called by the first function. This is explained on pages 118-121 of the "blue book", "The New S Language" by Becker, Chambers and Wilks, and in the TIBCO Spotfire S+ 8.1 Programmer's Guide, in the section titled "Matching Names and Values".
For example:
a <- 3
foo <- function(x)
{
a <- x
bar()
}
bar <- function()
{
a
}
foo(7)
The result is 3, not 7.
The bar() function looks for objects in:
(1) it's own frame (any objects already defined inside bar)
(2) frame 1 (the "expression frame", a temporary frame in memory)
(3) frame 0 (the "session frame", a temporary frame in memory that persists between commands but disappears when you exit S+)
(4) permanent databases
'bar' does not look in anywhere between frame 1 and its own frame (in the above example, foo has frame 2 and bar has frame 3. For another example, if you call f, which calls foo, which calls bar, then f has frame 2, foo frame 3, and bar frame 4).
In other words, if you define something inside 'foo', it is invisible to 'bar'.
A frame is essentially a list of objects that a function has defined for its own use (but frame 0 and frame 1 are for universal use). For example, after "a <- x", then foo's frame contains 'a' and 'x'.
The exception is if you explicitly use:
get(<an object name>, <the number of the frame of foo>)
to specify where to look for the object that would otherwise be invisible.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~