Home > Back-end >  Pass a `chr` type argument and update the content of a nested function to return in R?
Pass a `chr` type argument and update the content of a nested function to return in R?

Time:01-09

For a nested function such as

fun_parent <- function(var) {
  fun_child <- function(x) {
    x   var
  }
  return(fun_child)
}

How can I customize the content of the returned function fun_child based on the input var? For example,

test <- fun_parent(var = "y")
print(test)

Desirable output:

function(x) {
  x   y
  }
<environment: xxxxxx>

Actual (undesirable) output:

function(x) {
    x   var
  }
<environment: 0x7f7f20126b70>

Any help would be greatly appreciated!

CodePudding user response:

If you need to pull a value from the object list based on a name given as a character, then the get function suffices.

 fun_parent <- function(var) {
     fun_child <- function(x) {
         x   get(var)
     }
     return(fun_child)
 }
 test <- fun_parent(var = "y")
 print(test)

#function(x) {
#        x   get(var)
#    }
#
#<environment: 0x5654f87a1ae8>
Y=6
test(4)
#[1] 10
z=10

test <- fun_parent(var = "z")
print(test)
#function(x) {
#        x   get(var)
#   }
#<bytecode: 0x5654f7eac430>
#<environment: 0x5654f7637bb8>
test(4)
#[1] 14

The value of z is not stored in the test function, only the name is.

 z = 20
 test(4)
[1] 24

But is you want to see how that happens you need to look at the environment itself since the body was not evaluated:

> test
function(x) {
        x   get(var)
    }
<bytecode: 0x5654f7eac430>
<environment: 0x5654f7637bb8>
> environment(test)$var
[1] "z"

I was a bit surprised that substituting "x" for the value of var succeeded, but I wasn't suprised that using an object name that I knew was in my workspace did work:

> environment(test)$var <- "x"
> test(4)
[1] 8
> x     # only existed in the parameter list but was stored in the environment
Error: object 'x' not found  
> environment(test)$var <- "y"
> test(4)
[1] 10

CodePudding user response:

One way to modify functions inside a factory is to use the base function body. As the input is a character here, as.name is used. If fun_parent(y), without the string, is required, replace as.name with substitute.

fun_parent <- function(var) {
  fun_child = function(x) x   var
  body(fun_child)[[3]] <- as.name(var)
  fun_child
}

y = 3
test = fun_parent("y")
test(1)
[1] 4

In this case this approach is likely not the best solution, as pointed out by Dan Adams above. I would add to that that the created function is impure - it relies on scoping to find an y, which may lead to unintended behavior.

CodePudding user response:

I asked a related but not identical question here. Here are two options using bquote:

f <- function(varname) {
  removeSource(eval(bquote(function(x) x   .(as.name(varname)))))
}
f("y")
function (x) 
x   y
<environment: 0x1128976b0>
op <- options(keep.source = FALSE)
g <- function(varname) {
  eval(bquote(function(x) x   .(as.name(varname))))
}
options(op)
g("y")
function (x) 
x   y
<environment: 0x110491580>
  •  Tags:  
  • Related