I am hunting for ways to monitor when an object is updated, and do something (e.g. redraw a plot, print a message).
The ideal would be a generic function like:
watch(obj, fn)
where fn is called whenever obj is changed.
Or, are there any equivalents to View or plot which could do this?
makeActiveBinding is just what I was looking for, suggested by Ben Bolker. A quick example:
makeActiveBinding("visibull", function(x) {
if (! missing(x)) {
.invisibull <<- x;
View(.invisibull)
} else .invisibull
}, .GlobalEnv)
visibull <- data.frame(a=1:10, b=1:10)
visibull
visibull$a <- visibull$a + 1
.invisibull
Related
I'm trying to read a function call as a string and evaluate this function within another function. I'm using eval(parse(text = )) to evaluate the string. The function I'm calling in the string doesn't seem to have access to the environment in which it is nested. In the code below, my "isgreater" function finds the object y, defined in the global environment, but can't find the object x, defined within the function. Does anybody know why, and how to get around this? I have already tried adding the argument envir = .GlobalEnv to both of my evals, to no avail.
str <- "isgreater(y)"
isgreater <- function(y) {
return(eval(y > x))
}
y <- 4
test <- function() {
x <- 3
return(eval(parse(text = str)))
}
test()
Error:
Error in eval(y > x) : object 'x' not found
Thanks to #MrFlick and #r2evans for their useful and thought-provoking comments. As far as a solution, I've found that this code works. x must be passed into the function and cannot be a default value. In the code below, my function generates a list of results with the x variable being changed within the function. If anyone knows why this is, I would love to know.
str <- "isgreater(y, x)"
isgreater <- function(y, x) {
return(eval(y > x))
}
y <- 50
test <- function() {
list <- list()
for(i in 1:100) {
x <- i
bool <- eval(parse(text = str))
list <- append(list, bool)
}
return(list)
}
test()
After considering the points made by #r2evans, I have elected to change my approach to the problem so that I do not arrive at this string-parsing step. Thanks a lot, everyone.
I offer the following code, not as a solution, but rather as an insight into how R "works". The code does things that are quite dangerous and should only be examined for its demonstration of how to assert a value for x. Unfortunately, that assertion does destroy the x-value of 3 inside the isgreater-function:
str <- "isgreater(y)"
isgreater <- function(y) {
return(eval( y > x ))
}
y <- 4
test <- function() {
environment(isgreater)$x <- 5
return(eval(parse(text = str) ))
}
test()
#[1] FALSE
The environment<- function is used in the R6 programming paradigm. Take a look at ?R6 if you are interested in working with a more object-oriented set of structures and syntax. (I will note that when I first ran your code, there was an object named x in my workspace and some of my efforts were able to succeed to the extent of not throwing an error, but they were finding that length-10000 vector and filling up my console with logical results until I escaped the console. Yet another argument for passing both x and y to isgreater.)
I have a simple recursion function in R that would modify its input arguments. I use eval.parent(substitute()) in order to apply the changes on the arguments, However I get this error:
"invalid (do_set) left-hand side to assignment "
Does anyone know how to fix this error?
Here is my function:
decompose_clade=function(ind,Small_clades_ind,Remove_tips,Clades){
Ch=Children(tree,ind)
if(Ch[1]<length(tree$tip.label)){
l=Remove_tips
l=c(l,Ch[1])
eval.parent(substitute(Remove_tips=l))
print("tip added=")
print(Ch[1])
}else if(length(extract.clade(tree,Ch[1])$tip.label)<4){
s=Small_clades_ind
s=c(s,Ch[1])
eval.parent(substitute(Small_clades_ind=s))
print("small clade added=")
print(Ch[1])
}else if(length(extract.clade(tree,Ch[1])$tip.label)>80){
decompose_clade(Ch[1],Small_clades_ind,Remove_tips,Clades)
print("function calls again")
}else{
m=Clades
m=c(m,Ch[1])
eval.parent(substitute(Clades<-m))
print("a clade added=")
print(Ch[1])
}
}
There is no easy way to have pass-by-reference behaviours in R, with only one exception: environment. I'm not sure if environment suits your need, you can give it a try:
modify_input <- function(x){
x$z <- 1
}
x <- new.env(parent = emptyenv())
modify_input(x)
x$z
As to the usage, environment supports e$z and e[["z"]] and length(e) just like list, but it doesn't support e[[1]] and things like that. You can think of environment as a dictionary and elements in it have no order. If you want to list all the elements in an environment, you can use ls. And there are ways to transform environment to list (as.list) and vise versa (list2env). Hope it can help.
May be you can follow the following approach.
x <- 1
fn <- function() {
x <<- 2
}
Now x is 2.
Consider the following functions
f1 <- function(x) {
# do something
x
}
f2 <- function(x) {
# do something
invisible(x)
}
Suppose I call these two functions separately and save their values.
a <- f1(1)
b <- f2(2)
Is there a way to know if a and b are invisibly returned?
The motivation is that I want to create a function in which if a value is invisibly returned the function also wants to return the value invisibly.
There's withVisible, which lets you do this:
> f3 = function(f, x){
v=withVisible(f(x))
if(v$visible){
return(v$value)
}else{
return(invisible(v$value))
}
}
> f3(f1,1)
[1] 1
> f3(f2,1)
There's no way of doing it once you've got a and b, since identical(a,b) is TRUE. You can only call withVisible on an expression. Unless something lazy or promisy is going on.
A possible alternative to Spacedman's (proper :-) ) solution is to put the following line inside your "outer" function.
if (grepl('invisible', body(inner_function) ) ) return(invisible(data)) else return(data)
Obviously this will fail if you do something creative like naming a variable "pinvisible"
I'm trying to create an enclosing function which will:
process some data,
cat() results of that data,
request user input (ie, via readline() ) based on the results of that cat(),
then return a function where one of the argument defaults of the returned function is the value inputted by readline().
Additionally, I'd like the remaining default values of the arguments of the returned function to be user-interpretable. That is, I don't want the defaults to be variable names of variables hidden in the parent environment (this stipulation precludes simple argument passing). Specifically, I'd like arg() to return actual evaluated numbers, etc.
I've cooked up this solution below, but it feels clunky and awkward. Is there a more elegant way of approaching this?
top <- function(year=1990, n.times=NULL){
if(is.null(n.times)){
###in the real function, data would be processed here
###results would be returned via cat and
###the user is prompted return values that reflect a decision
###made from the processed data
n.times <- as.numeric(readline("how many times?"))
}
out <- function(year, n.times){
###in the real function, this is where most of the work would happen
rep(year, n.times)
}
###this entire section below is clunky.
if( !identical( names(formals()), names(formals(out)) ) ){
stop("internal error: mismatching formals")
}
pass.formals <- setdiff( names(formals()), "n.times")
formals(out)[pass.formals] <- formals()[pass.formals]
formals(out)$n.times <- n.times
out
}
x <- top()
x
It looks generally OK to me; there's only a few things I'd do differently.
Is there any reason that the parameters of top() and out() seem to correspond in some
way? ie, do you need the identical check? Not sure, so I took it out. This seems to do what you
want, and is slightly shorter:
top <- function(year=1990, n.times=NULL){
if (is.null(n.times)) {
n.times <- as.numeric(readline("how many times?"))
}
out <- function(year, n.times) {
rep(year, n.times)
}
out.formals = formals(out)
out.formals['n.times'] = n.times
formals(out) = out.formals
out
}
edit: And if you want to use super R magic, you can write
top <- function(year=1990, n.times=NULL){
if (is.null(n.times)) {
n.times <- as.numeric(readline("how many times?"))
}
`formals<-`(
function() {
rep(year, n.times)
},
value=list(year=alist(x=)$x, n.times=n.times)
)
}
edit: You could also use something like DWin suggested (though I couldn't
get it to work with substitute):
out = function(year, n.times) {
rep(year, n.times)
}
`formals<-`(out, v=`[[<-`(formals(out), 'n.times', n.times))
Or using bquote:
eval(bquote(
function(year, n.times=.(n.times)) {
rep(year, n.times)
}
)[-4L])
You have so many options.
Is it possible to add an on.exit expr to the parent call? If so, how?
For example, say that parentOnExit(expr) is a function implementing this. Then for the following code:
f <- function() {
parentOnExit(print("B"))
print("A")
}
I want to see "A" printed, then "B".
Background: What brought this to mind was the following... we have a collection of functions, some of which call others, which require a resource that should be shared from the topmost call down and which also should be closed upon exiting the topmost function. Eg, a connection to a remote server which is expensive to open. One pattern for this is:
foo <- function(r=NULL) {
if (is.null(r)) { # If we weren't passed open connection, open one
r <- openR()
on.exit(close(r))
}
bar(r=r) # Pass the open connection down
}
I was hoping to abstract those three lines down to:
r <- openIfNull(r) # Magically call on.exit(close(r)) in scope of caller
Now that I think about it though, perhaps it's worth some repeated code to avoid anything too magical. But still I'm curious about the answer to my original question. Thank you!
I have seen in this recent mail discussion (https://stat.ethz.ch/pipermail/r-devel/2013-November/067874.html) that you can use do.call for this:
f <- function() { do.call("on.exit", list(quote(cat('ONEXIT!\n'))), envir = parent.frame()); 42 }
g <- function() { x <- f(); cat('Not yet!\n'); x }
g()
#Not yet!
#ONEXIT!
#[1] 42
Using this feature and an additional ugly trick to pass the R connection object to the caller environment, it seems to solve the problem:
openR <- function(id = "connection1") {
message('openR():', id)
list(id)
}
closeR <- function(r) {
message('closeR():', r[[1]])
}
openRIfNull <- function(r) {
if (length(r)) return(r)
# create the connection
r <- openR("openRIfNull")
# save it in the parent call environment
parent_env <- parent.frame()
assign("..openRIfNull_r_connection..", r, envir = parent_env)
do.call("on.exit", list(quote(closeR(..openRIfNull_r_connection..))), envir = parent_env)
r
}
foo <- function(r = NULL) {
message('entered foo()')
r <- openRIfNull(r)
bar(r = r) # Pass the open connection down
message('exited foo()')
}
bar <- function(r) {
message('bar()')
}
example use:
foo()
# entered foo()
# openR():openRIfNull
# bar()
# exited foo()
# closeR():openRIfNull
foo(openR('before'))
# entered foo()
# openR():before
# bar()
# exited foo()
I was intrigued by the problem and tried a couple of ways to solve it. Unfortunately, they didn't work. I'm therefore inclined to believe that it can't be done. ...But someone else might be able to prove me wrong!
Anyway, I though I'd post my failed attempts so that they are recorded. I made them so that they would print "ONEXIT!" after "Not yet!" if they worked...
1 - First, simply try to evaluate the on.exit in the parent environment:
f <- function() { eval(on.exit(cat('ONEXIT!\n')), parent.frame()); 42 }
g <- function() { x<-f(); cat('Not yet!\n'); x }
g() # Nope, doesn't work!
This doesn't work, probably because the on.exit function adds stuff to the current stack frame, not the current environment.
2 - Step up the game and try to return an expression that is evaluated by the caller:
f <- function() { quote( {on.exit(cat('ONEXIT!\n')); 42}) }
g <- function() { x<-eval(f()); cat('Not yet!\n'); x }
g() # Nope, doesn't work!
This doesn't work either, probably because eval has its own stack frame, different from g.
3 - Bring my A-game, and try to rely on lazy evaluation:
h <- function(x) sys.frame(sys.nframe())
f <- function() { h({cat('Registering\n');on.exit(cat("ONEXIT!\n"));42}) }
g <- function() { x<-f()$x; cat('Not yet!\n'); x }
g() # Worse, "ONEXIT!" is never printed...
This one returns an environment to the caller, and when the caller accesses "x" in it, the expression including on.exit is evaluated. ...But it seems on.exit does not register at all in this case.
4 - Hmm. There is one way that might still work: a .Call to some C code that calls on.exit. It might be that calling C won't add another stack frame... This is a bit too complex for me to test now, but maybe some RAPI/RCpp guru could give it a shot?
I remain confused, but if Tommy can't do it, I suspect I won't be able to either. This does the first task and since it seemed so simple I thought I must be missing something:
f <- function() {
on.exit(print("B"))
print("A")
}
Second effort:
txtB <- textConnection("test b")
txt <-textConnection("test A")
f <- function(con) { df <- read.table(con);
if( isOpen(txtB)){ print("B open")
eval( close(txtB), env=.GlobalEnv ) }
return(df) }
txtB #just to make sure it's still open
# description class mode text
# "\"test b\"" "textConnection" "r" "text"
# opened can read can write
# "opened" "yes" "no"
dat <- f(txt); dat
#[1] "B open"
# V1 V2
#1 test A
txtB
#Error in summary.connection(x) : invalid connection
(OK, I edited it to close a connection within the calling environment.)
So what am I missing? (It wasn't clear to me as I tested this that connections actually have environments.)
Though this question is quite old, there is a simple fix for any future visitors:
Use add=TRUE (I don't find the documentation very clear.)
f <- function() {
on.exit(expr = print("B"),
add = TRUE)
print("A")
}
A another solution is using withr::defer() which has more options and better documentation.
The vignette is especially helpful.