Re: [S] calls to trellis in functions with varaible args

Prof Brian Ripley (ripley@stats.ox.ac.uk)
Thu, 18 Jun 1998 17:26:09 +0100 (BST)


Ed Malthouse <ecm@skew2.kellogg.nwu.edu> wrote:

> I use Splus version 3.4 under HP-UX 9.0.5
>
> I would like to call trellis functions from within a function and
> pass arguments to the trellis function that I specify as arguments
> to my function. Here's a simple example:
>
> > plotit
> function(mydata, filename)
> {
> trellis.device(postscript, file = filename)
> print(bwplot(y ~ x, data = mydata))
> dev.off()
> }
>
> When I run this I get an error message:
> > plotit(ans, "tmp.ps")
> Error in trellis.device(postscript, file = filenam..: Object
> "filename" not
> found
> Dumped
>
> If I use the postscript command it works fine:
> > plotit2
> function(mydata, filename)
> {
> postscript(file = filename)
> print(bwplot(y ~ x, data = mydata))
> dev.off()
> }
> > plotit2(ans, "tmp.ps")
> Generated postscript file "tmp.ps".
>
> What is going on here? How can I make this work? I have the same
> problem if I try to pass something to the bwplot command, e.g.,
> bwplot(y~x, data=mydata, subset=(z==1))
> where z is an argument to my function, e.g., plotit.
>

It is those scope rules again. Variables in the parent function (like
its arguments are not visible inside a function. You need to assign
them in frame 1, e.g.

plotit <- function(mydata, filename)
{
assign("filename", filename, f=1)
trellis.device(postscript, file = filename)
print(bwplot(y ~ x, data = mydata))
dev.off()
}

It works for postscript as that is a direct call, but not for
trellis.device that calls postscript. Try

> plotit(ans, "tmp.ps")
Error in trellis.device(postscript, file = filenam..: Object "filename" not
found
Dumped
> traceback()
Message: Object "filename" not found
5: postscript(file = filename, colors = c(0, 0.6, 0.3, 0.9, 0.95, 0.7, 0,
4: eval(device.call)
3: trellis.device(postscript, file = filename)
2: plotit(ans, "tmp.ps")
1:
and frame 5 is being evaluated in frame 3 not frame 2 where "filename" is.

The writer of trellis.device should have used eval(device.call, sys.parent())
I believe. As the V&R2 Programming Complements say:

\item Remember the scope rules (page~154); in particular consider if
your functions will work when called from within a function.

Within a function, a call to \sfn{eval} probably needs a non-default
value for \sfn{local}.

Functions defined within functions do not have automatic access to
local variables of the parent.

Functions such as \sfn{tapply}, \sfn{apply} and \sfn{nlminb} make
provision for extra arguments to be passed down to their function
arguments: they are more often needed than is commonly realised.

If you understand eval and parse you can do this without calls
to assign() by constructing the S code using paste, then eval a parsed
version. For example.

plotit <- function(mydata, filename)
{
expr <- paste("trellis.device(postscript, file = ",
deparse(filename), ")", sep="")
eval(parse(text=expr))
print(bwplot(y ~ x, data = mydata))
dev.off()
}

If it is not obvious to you why I needed deparse here, try it both
ways for yourself.

-- 
Brian D. Ripley,                  ripley@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 272860 (secr)
Oxford OX1 3TG, UK                Fax:  +44 1865 272595

----------------------------------------------------------------------- This message was distributed by s-news@wubios.wustl.edu. To unsubscribe send e-mail to s-news-request@wubios.wustl.edu with the BODY of the message: unsubscribe s-news