[R] eval parent.frame() twice

Duncan Murdoch murdoch at stats.uwo.ca
Fri Aug 7 14:56:35 CEST 2009


On 8/7/2009 7:36 AM, Rune Schjellerup Philosof wrote:
> Peter Dalgaard skrev:
>> Rune Schjellerup Philosof wrote:
>>   
>>> Hi
>>>
>>> I want to use a function (update) that in its body uses
>>> eval(call, parent.frame())
>>>
>>> I would like to use this function in a function that does not contain
>>> the variables referred to in 'call'. Those variables are instead in the
>>> parent.frame() of my function (named 'second' below)
>>>
>>> Like this:
>>> a <- 2
>>>
>>> evalu <- function(obj) {
>>>  call <- obj$call
>>>  eval(call, parent.frame())
>>> }
>>> first <- function() {
>>>  a <- 3
>>>  obj <- list(call=expression(a))
>>>  second(obj)
>>> }
>>> second <- function(obj) {
>>>  eval(evalu(obj), parent.frame())
>>> }
>>>
>>> first() #returns 2, but I want 3.
>>>
>>>
>>> How do I change 'second' such that the value of 'a' from 'first' is
>>> returned?
>>>
>>>     
>>
>> You could use the "n" argument to parent.frame (or eval.parent) inside
>> "evalu" and go two generations back instead of one. Somewhat neater, you
>> could pass the desired environment directly to evalu, as in
>>
>> e <- parent.frame()
>> evalu(obj, e)
>>
>> (I'm not sure it is actually needed to separate out the calculation of
>> "e", but I tend to be paranoid about evaluating parent.frame() in
>> function arguments.)
>>
>>
>>   
> 
> Thanks for the answer, but it isn't what I need.
> 'evalu' is a replacement for a function that I cannot change
> (update.default).
> My question is: How do I accomplish this by only changing 'second'?

I don't think there is any clean way to do that, but you can call 
update.default with evaluate=FALSE, and then evaluate the result in 
whatever environment you like.

If you are determined to do it uncleanly, then one way would be to 
create a little function that has no names that should clash to call 
update.default, and set its environment to the environment in which you 
want the evaluation to happen.  E.g.

second <- function(obj, formula) {
   my.update.default <- function(.safe.obj, .safe.formula) {
       update.default(.safe.obj, .safe.formula)
   }
   environment(my.update.default) <- parent.frame()
   my.update.default(obj, formula)
}

If your user happens to use ".safe.obj" or ".safe.formula" as the name 
of a variable in their model, this will fail, but otherwise it should 
work, because update.default will look at the locals of 
my.update.default first, before it goes to the parent, which is 
parent.frame(). If you need to pass more arguments to update.default you 
need to worry about their name clashes as well.

Personally, I'd stick to the clean solution.  You could try lobbying for 
a change to update.default, but I think you'd fail, because the clean 
solution is available.

Duncan Murdoch

 >>> }




More information about the R-help mailing list