[R] Scoping rules

Prof Brian Ripley ripley at stats.ox.ac.uk
Thu Oct 9 04:28:56 CEST 2003


On Wed, 8 Oct 2003, Roger D. Peng wrote:

> It seems like you want in fnB
> 
> get(AA$first, envir = parent.frame(1))
> 
> but I'm entirely clear on why your original function doesn't work.  My 
> understanding was that get() should search through the parent frames.

Where did you get that idea from?  Argument `inherits' to get() defaults
to TRUE and is defined as

inherits: should the enclosing frames of the environment be inspected?

Note `enclosing', not `parent'.  So the normal R scope rules apply when
looking for an object from the frame of fnB, which as its environment (aka
enclosing frame) is the workspace (.GlobalEnv) is to look in the local
frame and then along the search path.

[This does seem a fairly common misconception, so if you do have any idea 
in which document it arises it would be good to know.]

> Peter Alspach wrote:
> > Dear List members:
> > 
> > I'm using R1.7.1 (Windows 2000) and having difficulty with scoping. 
> > I've studied the FAQ and on-line manuals and think I have identified
> > the
> > source of my difficulty, but cannot work out the solution.
> > 
> > For the purposes of illustration.  I have three functions as defined
> > below:
> > 
> > fnA <- function(my.x)
> > {
> >   list(first=as.character(substitute(my.x)), second=sqrt(my.x))
> > }
> > 
> > fnB <- function(AA)
> > {
> >   tmp.x <- get(AA$first)
> >   tmp.x^2
> > }
> > 
> > fnC <- function()
> > {
> >   x <- 1:2
> >   y <- fnA(x)
> >   z <- fnB(y)
> >   c(x,y,z)
> > }
> > 
> > fnA() has a vector as an argument and returns the name of the vector
> > and the square root of its elements in a list.  fn(B) takes the result
> > of fn(A) as its argument, gets the appropriate vector and computes the
> > square of its elements.  These work fine when called at the command
> > line.
> > 
> > fnC() defines a local vector x and calls fnA() which operates on this
> > vector.  Then fnB() is called, but it operates on a global vector x in
> > GlobalEnv (or returns an error is x doesn't exist there) - but I want
> > it
> > to operate on the local vector.

I am not sure what you really want to do here, but R works best if you 
pass functions an object to work on, and not the name of an object.
Normally this sort of thing is best avoided, but if it is really needed it 
is normally simpler to find the object in the calling function (your fnC).

> > I think this is related to the enclosing environment of all three
> > functions being GlobalEnv (since they were created at the command
> > line),
> > but the parent environment of fnB() being different when invoked from
> > within fnC().
> > 
> > My questions:
> > 
> > 1  Have I correctly understood the issue ?
> > 2  How do I make fnB() operate on the local vector rather than the
> > global one ?
> > 3  And, as an aside, I have used as.character(substitute(my.x)) to
> > pass
> > the name - but deparse(substitute(my.x)) also works.  Is there any
> > reason to prefer one over the other?

deparse() is preferred.  One subtle reason is what happens with very long 
expressions (and note deparse may give more than one line of output), 
but the main reason is what happens with expressions such as calls:

> fn1 <- function(x) as.character(substitute(x))
> fn2 <- function(x) deparse(substitute(x))
> fn1(log(x))
[1] "log" "x"  
> fn2(log(x))
[1] "log(x)"


It is normally dangerous to use get() on the result of 
deparse(substitute()), as the latter may be the character representation 
of an expression and not a simple name.

-- 
Brian D. Ripley,                  ripley at stats.ox.ac.uk
Professor of Applied Statistics,  http://www.stats.ox.ac.uk/~ripley/
University of Oxford,             Tel:  +44 1865 272861 (self)
1 South Parks Road,                     +44 1865 272866 (PA)
Oxford OX1 3TG, UK                Fax:  +44 1865 272595




More information about the R-help mailing list