Evaluating a function call constructed from a string - r

Say I have two functions whose names are contained within a vector. I would like to test if each function works.
My approach, which I readily admit could be wrong, was to loop through the vector then paste () to the function name. But then I realized I have no idea how to evaluate the function call which is current stored as a string. Here is a reprex:
func1 <- function(){
message("func1 works")
}
func2 <- function(){
message("func2 works")
}
fv <- c("func1","func2")
for(i in seq_along(fv)){
fv_func <- paste0(fv[i],"()")
print(fv_func)
}
[1] "func1()"
[1] "func2()"
So in this context I am asking how to evaluate func1() and func2() though the ultimate goal is to evaluate function whose names are stored in a vector - meaning i'm open to better solution.

If you have the names of the functions as strings you can get() them:
fv <- c("func1","func2")
for(i in seq_along(fv)){
fv_func <- get(fv[i])
# Can just call normally, no need to paste () on
fv_func()
}

Try either of these:
out <- lapply(fv, do.call, list())
out <- lapply(fv, function(f) match.fun(f)())

We can do it in one line without a for loop if we use vectorised eval(parse):
eval(parse(text = paste0(fv,'()')))

Related

Using R, variadically checking if `exists` [duplicate]

I stacked with trying to pass variable through few functions, and on the final function I want to get the name of the original variable. But it seems like substitute function in R looked only in "local" environment, or just for one level up. Well, let me explain it by code:
fun1 <- function (some_variable) {deparse(substitute(some_variable)}
fun2 <- function (var_pass) { fun1 (var_pass) }
my_var <- c(1,2) # I want to get 'my_var' in the end
fun2 (my_var) # > "var_pass"
Well, it seems like we printing the name of variable that only pass to the fun1. Documentation of the substitute tells us, that we can use env argument, to specify where we can look. But by passing .Global or .BaseNamespaceEnv as an argument to substitute I got even more strange results - "some_variable"
I believe that answer is in this function with using env argument, so, could you please explain me how it works and how can I get what I need. Thanks in advance!
I suggest you consider passing optional name value to these functions. I say this because it seems like you really want to use the name as a label for something in the end result; so it's not really the variable itself that matters so much as its name. You could do
fun1 <- function (some_variable, name=deparse(substitute(some_variable))) {
name
}
fun2 <- function (var_pass, name=deparse(substitute(var_pass))) {
fun1 (var_pass, name)
}
my_var <- c(1,2)
fun2(my_var)
# [1] "my_var"
fun1(my_var)
# [1] "my_var"
This way if you end up having some odd variable name and what to give a better name to a result, you at least have the option. And by default it should do what you want without having to require the name parameter.
One hack, probably not the best way:
fun2 <- function (var_pass) { fun1 (deparse(substitute(var_pass))) }
fun1 <- function (some_variable) {(some_variable))}
fun2(my_var)
# "my_var"
And you could run get on that. But as Paul H, suggests, there are better ways to track variables.
Another approach I'd like to suggest is to use rlang::enexpr.
The main advantage is that we don't need to carry the original variable name in a parameter. The downside is that we have to deal with expressions which are slightly trickier to use.
> fun1 <- function (some_variable) {
message("Entering fun1")
rlang::enexpr(some_variable)
}
> fun2 <- function (var_pass) {
message("Entering fun2")
eval(parse(text=paste0("fun1(", rlang::enexpr(var_pass), ")")))
}
> my_var <- c(1, 2)
> fun1(my_var)
#Entering fun1
my_var
> fun2(my_var)
#Entering fun2
#Entering fun1
my_var
The trick here is that we have to evaluate the argument name in fun2 and build the call to fun1 as a character. If we were to simply call fun1 with enexpr(var_pass), we would loose the notion of fun2's variable name, because enexpr(var_pass) would never be evaluated in fun2:
> bad_fun2 <- function (var_pass) {
message("Entering bad fun2")
fun1(rlang::enexpr(var_pass))
}
> bad_fun2(my_var)
#Entering bad fun2
#Entering fun1
rlang::enexpr(var_pass)
On top of that, note that neither fun1 nor fun2 return variable names as character vectors. The returned object is of class name (and can of course be coerced to character).
The bright side is that you can use eval directly on it.
> ret <- fun2(my_var)
#Entering fun2
#Entering fun1
> as.character(ret)
[1] "my_var"
> class(ret)
[1] "name"
> eval(ret)
[1] 1 2

R: eval parse function call not accessing correct environments

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.)

Putting user-defined on a list in for loop

I have problems storing user defined functions in R list when they are put on it in a for loop.
I have to define some segment-specific functions based on some parameters, so I create functions and put them on a list looping through segments with for-loop. The problem is I get same function everywhere on a result list.
The code looks like this:
n <- 100
segmenty <- 1:n
segment_functions <- list()
for (i in segmenty){
segment_functions[[i]] <- function(){return(i)}
}
When i run the code what I get is the same function (last created in the loop) for all indexes:
## for all k
segment_functions[[k]]()
[1] 100
There is no problem when I put the functions on list manually e.g.
segment_functions[[1]] <- function(){return(1)}
segment_functions[[2]] <- function(){return(2)}
segment_functions[[3]] <- function(){return(3)}
works just fine.
I honsetly have no idea what's wrong. Could you help?
You need to use the force function to ensure that the evaluation of i is done during the assignment into the list:
n <- 100
segmenty <- 1:n
segment_functions <- list()
f <- function(i) { force(i); function() return(i) }
for (i in segmenty){
segment_functions[[i]] <- f(i)
}
I'd use lapply and capture i in a clousre of the wrapper:
segment_functions <- lapply(1:100, function(i) function() i)

Getting names from ... (dots)

In improving an rbind method, I'd like to extract the names of the objects passed to it so that I might generate unique IDs from those.
I've tried all.names(match.call()) but that just gives me:
[1] "rbind" "deparse.level" "..1" "..2"
Generic example:
rbind.test <- function(...) {
dots <- list(...)
all.names(match.call())
}
t1 <- t2 <- ""
class(t1) <- class(t2) <- "test"
> rbind(t1,t2)
[1] "rbind" "deparse.level" "..1" "..2"
Whereas I'd like to be able to retrieve c("t1","t2").
I'm aware that in general one cannot retrieve the names of objects passed to functions, but it seems like with ... it might be possible, as substitute(...) returns t1 in the above example.
I picked this one up from Bill Dunlap on the R Help List Serve:
rbind.test <- function(...) {
sapply(substitute(...()), as.character)
}
I think this gives you what you want.
Using the guidance here How to use R's ellipsis feature when writing your own function?
eg substitute(list(...))
and combining with with as.character
rbind.test <- function(...) {
.x <- as.list(substitute(list(...)))[-1]
as.character(.x)
}
you can also use
rbind.test <- function(...){as.character(match.call(expand.dots = F)$...)}

Access all function arguments in R

I've a function f() that has some named parameters. It calls a function g() and I want to pass all f's parameters to it. Is this possible?
Using ... just covers the extra arguments:
f=function(a,callback,b,c,d,...){
z=a-b
callback(z,...)
}
g=function(z,...){
print(list(...)) #Only shows $e
print(z) #-1
print(a,b,c,d) #'a' not found
}
f(1,g,2,3,d=4,e=5);
I thought formals() was the answer, but it just seems to be argument names, not their values!
f=function(a,callback,b,c,d,...){
z=a-b
callback(z,formals())
}
g=function(z,...){
args=list(...)[[1]]
print(args$a) #(no output)
print(class(args$a)) #"name"
}
f(1,g,2,3,d=4,e=5);
Is it possible? Thanks.
Well, something like this is certainly possible. You should just figure our for yourself in which frame / point you'd like to evaluate the arguments of f which are then forwarded to g.
The typical procedure consists of match.call() call inside f to actually record the call expression which f was called with, then changing the call expression as it should be convenient for you (e.g. filtering out unnecessary args, adding new, etc.) and then evaluation of the new call expression via eval() call. So, something like this should (almost) work:
f <- function(a, callback, b, c, d, ...) {
# Grab the "f" call expression
fcall <- match.call(expand.dots = FALSE)
# Construct the new call expression
fcall[[1]] <- callback
# Filter out / add new args
fcall$callback <- NULL
fcall$z <- z
# Do the call
eval(fcall, parent.frame())
}

Resources