[R] conditional assignments and calculations
Michael Lachmann
lachmann at eva.mpg.de
Mon Oct 4 11:34:32 CEST 2004
Thank you!
Now the conditional assignments work almost prefectly.
The current code is at the bottom of the message.
Now I can do
depends[A,B]=B+9
and this will be executed only if B was more recently updated then A, and if the last time A was updated, the expression used was 'B+9'.
And it is also possible to do
depends[A,file="table.txt"]=read.table("table.txt")
and A will only be read again if the file "table.txt" was updated more recently than it was read last time.
I have only a few remaining questions:
1. I added attributes to variables, "last.updated", and "update.expression". If the variables are just regular vectors/lists, then when I print them, I also see these attributes. Is it somehow possible to have these attributes hidden?
2. I defined an operator %set%, which is supposed to work just like "<-" and "=", but also updates "last.updated" and "update.expression". It would be best to really replace "<-" and "=" with this operator, and then any update of a variable will be registered. This doesn't seem to work, because if I define "<-" as
function(x,value)
{
v=as.character(as.expression(substitute(value)))
eval.parent(substitute(.Primitive("<-")(x,value)))
eval.parent(substitute(attr(x,"last.updated")<-Sys.time()))
eval.parent(substitute(attr(x,"update.expression")<-v))
x
}
then this function calls "attr<-", which then calls "<-" leading to infinite recursion. Is there a way to set an attribute on a variable without using attr<- ?
Or maybe it is a bad idea to redefine "<-" in the first place...
Thank you very much!
Michael
Gabor Grothendieck wrote:
>Michael Lachmann <lachmann <at> eva.mpg.de> writes:
>
>:
>: Hello!
>:
>: I am using the TeXmacs interface to R. (Though I encountered a similar
>: problem when using Sweave)
>: In doing calculations I often ecounter this scenario: I'll have some
>: calculations in my file:
>: --
>: A=read.lots.of.data()
>:
>: B=huge.calculation.on(A)
>:
>: C=another.calculation.on(B)
>: --
>: Now, if A has already been read, I don't need to re-read it. If B has
>: already been calculated, I don't need to recalculate it. But I would
>: like to be able to just press 'enter' on each of them.
>:
>: So, I would like R to somehow figure out dependencies (a bit like in
>: Makefiles)
>:
>: I implemented something like this with the following functions
...
>: But this solution is quite ugly, because of several problems:
>:
>: 1. To call 'depends(A,B)=f(B)' the first time, A has to already exist,
>: otherwise I get an error (before I enter the "depends<-" function.)
>
>The technique used to implement mulitple return values shown in
>
> http://tolstoy.newcastle.edu.au/R/help/04/06/1406.html
>
>could be adapted to this problem. Using that technique the code would
>look like this:
>
> depends[A,B] <- f(B)
>
>and A would not have to pre-exist.
>
>You define a structure with a class of depends, say:
>
> depends <- structure(NA, class = "depends")
>
>and then define the [<-.depends action on that structure
>in an analogous way to what was done there.
>
>: 2. I would also like to have a convenient way to do
>: "if( !exists(A) ) { A=read.lots.of.data(); A=touch(A) }"
>: maybe something like:
>: depends(A)<-read.lots.of.data()
>: But that doesn't work, because of 1.
>: or
>: A %set% read.lots.data()
>: But that doesn't work, because I haven't figured out a way for a
>: function to change one of its variables.
>: (Maybe I could do A=A %set read.lots.of.data(), but that is really ugly...)
>
>
>Is this what you want?
>
>R> f <- function(x,v) assign(as.character(substitute(x)), v, parent.frame())
>R> x # x does not exist
>Error: Object "x" not found
>R> f(x,3)
>R> x # now it does
>[1] 3
>
><<- can be used if the eval.parent is a third way (see #3 below).
>
>: 3. It would be nice to be able to do touch(A) instead of A=touch(A)
>
>touch <- function(x)
> eval.parent(substitute(attr(x,"last.updated")<-Sys.time()))
>x <- 3
>touch(x)
>
>:
>: 4. If I modify A without calling 'A=touch(A)', then B will not be
>: updated next time I call 'depends(B,A)=huge.calculation.on(A)'. So it
>: would be nice to have the variable's 'last updated' time updated
>: automatically. (Though then it is a bit problematic to decide what the
>: 'last updated' time should be for variables loaded from a file...)
>
>If its done in a function you could use on.exit to ensure that it gets
>updated when leaving the function.
>
touch=function(...,l=list())
{
args <- as.list(match.call())
if( "l" %in% names(args) ) {
argsl=as.list(args[[length(args)]][-1])
argsl=sapply(argsl,function(x) parse(text=x)[[1]])
args=args[-length(args)]
} else { argsl=c() }
args=c(args[-1],argsl)
for( i in 1:length(args)) {
eval.parent(substitute(attr(x,"last.updated")<-Sys.time(),
list(x=args[[i]]) ))
}
}
last.updated=function(a) {
if( length(attr(a,"last.updated")) == 0 ) {
Sys.time()
} else {
attr(a,"last.updated")
}
}
"%set%"=function(x,value)
{
v=as.character(as.expression(substitute(value)))
eval.parent(substitute(.Primitive("<-")(x,value)))
eval.parent(substitute(attr(x,"last.updated")<-Sys.time()))
eval.parent(substitute(attr(x,"update.expression")<-v))
x
}
depends <- structure(NA,class="depends")
"[<-.depends" <- function(x,...,file=c(),value) {
v=as.character(as.expression(substitute(value)))
args <- as.list(match.call())
if( !exists(as.character(args[[3]] ), env=sys.frame(-1)) ) {
eval(substitute(x<-v,list(x=args[[3]],v=value)),env=sys.frame(-1))
eval(substitute(touch(x),list(x=args[[3]])),env=sys.frame(-1))
eval.parent(substitute(attr(x,"update.expression")<-v,list(x=args[[3]],v=v)))
} else {
lu=list()
xlu=eval(substitute(attr(x,"last.updated"),list(x=args[[3]]),env=sys.frame(-1)))
if( length(file) >0 ) {
lu=c(lapply(file,function(f) {
file.info(f)$mtime
}),lu)
args=args[-(length(args)-1)]
}
if( length(xlu)>0 ) {
lu=c(lapply((1:(length(args)-1))[-c(1:3)],function(i) {
eval(substitute(last.updated(x),list(x=args[[i]])),env=sys.frame(-1))
}),lu)
}
lu=prod(sapply(lu,function(x) x-xlu<0))
xv=eval(substitute(attr(x,"update.expression"),list(x=args[[3]]),env=sys.frame(-1)))
if( length(xv)*length(v) > 0 ) {
if( xv != v ) lu = 0
} else { lu=0}
if( (lu==0) ) {
eval(substitute(x<-v,list(x=args[[3]],v=value)),env=sys.frame(-1))
eval(substitute(touch(x),list(x=args[[3]])),env=sys.frame(-1))
eval.parent(substitute(attr(x,"update.expression")<-v,list(x=args[[3]],v=v)))
}
}
x
}
More information about the R-help
mailing list