Home > OS >  How to pass an object from outside a R-function to the function's body?
How to pass an object from outside a R-function to the function's body?

Time:11-11

I want to pass the content of an object that is defined outside a function to the function's body. My problem might be easier to understand with an example:

a <- 'hello world'
foo <- function() print(a)
rm(a)
foo()

Currently, this obviously throws an error, because a is not defined anymore when calling foo().

My desired outcome is that I want foo to look like function() print('hello world')

CodePudding user response:

1) local Capture a using a local like this. This fixes the value of a to what it was when foo was defined. Note that the a on the right hand side of a <- a is the a already defined whereas the a on the left hand side is a new object, also called a, that is defined within the local. No packages are used.

a <- 'hello world'
foo <- local({
  a <- a
  function() print(a)
})

rm(a)
foo()
## [1] "hello world"

2) substitute Another alternative is to substitute the value of a into the body of foo like this. This fixes a in foo to the value that it was when the substitute is run. No packages are used.

foo <- function() print(a)

a <- 'hello world'
body(foo) <- do.call("substitute", list(body(foo), list(a = a)))

rm(a)
foo()
## [1] "hello world"

3) function which generates foo Another possibility is to create a function which generates foo. Note that R uses lazy evaluation so we need to use force to ensure that the value of a at the time that foogen runs is used; otherwise, it will delay the evaluation until after a is removed causing an error. This fixes a in foo to the value that is passed to foogen. No packages are used.

foogen <- function(a) {
  force(a)
  function() print(a)
}

a <- 'hello world'
foo <- foogen(a)
rm(a)
foo()
## [1] "hello world"

4) proto The proto package can be used to create an object p containing both foo and a.

library(proto)

p <- proto(foo = function(.) print(a))

a <- 'hello world'
p$a <- a
rm(a)
p$foo()
## [1] "hello world"

CodePudding user response:

You do not need to use eval(parse(...)) here, or an external package. Using eval and parse is generally not a good idea for a few different reasons, including the amount of fun that a malicious user could have exploiting it. This is becoming increasingly relevant as R is used more and more in web apps that accept user input.

There are a few ways to do this in base R without eval(parse(...)) or any external dependencies. One approach is to have a function factory:

a <- 'hello world'
foo_maker <- function(x) as.function(list(x = a, quote(print(x))))

So now we can do:

foo <- foo_maker(a)
rm(a)
foo()
#> [1] "hello world"

CodePudding user response:

Just found out how to solve the problem using the whisker package and eval(parse(...)):

library(whisker)
a <- 'hello world'
foo <- eval(parse(text = whisker.render('function() print("{{b}}")', 
                                        data = list(b = a))))
foo()
rm(a)
foo()
  • Related