在函数内访问函数中的变量

时间:2018-06-23 15:59:28

标签: r function parameter-passing

在R中运行一个函数时,我在其中运行另一个函数。 我有这样的代码:

f_a <- function(b, c){
    return(b + c)
}

f_e <- function(){
    b = 2
    c = 2 
    d = f_a(b, c)
    print(d)
}

这很好。我想做的是不将变量 b, c传递给函数f_a。我想做这样的事情(会引发错误)

f_a <- function(){
    return(b + c)
}

f_e <- function(){
    b = 2
    c = 2
    d = f_a()
    print(d)
}

是否可以使用环境或搜索路径或其他任何方式来做到这一点?

4 个答案:

答案 0 :(得分:9)

我鼓励您阅读有关lexical scoping的信息, 但我认为避免写入大量变量的好方法可能是:

get_args_for <- function(fun, env = parent.frame(), inherits = FALSE, ..., dots) {
    potential <- names(formals(fun))

    if ("..." %in% potential) {
        if (missing(dots)) {
            # return everything from parent frame
            return(as.list(env))
        }
        else if (!is.list(dots)) {
            stop("If provided, 'dots' should be a list.")
        }

        potential <- setdiff(potential, "...")
    }

    # get all formal arguments that can be found in parent frame
    args <- mget(potential, env, ..., ifnotfound = list(NULL), inherits = inherits)
    # remove not found
    args <- args[sapply(args, Negate(is.null))]
    # return found args and dots
    c(args, dots)
}

f_a <- function(b, c = 0, ..., d = 1) {
    b <- b + 1
    c(b = b, c = c, d = d, ...)
}

f_e <- function() {
    b <- 2
    c <- 2
    arg_list <- get_args_for(f_a, dots = list(5))
    do.call(f_a, arg_list)
}

> f_e()
b c d   
3 2 1 5 

默认情况下设置inherits = FALSE可确保我们仅从指定的环境中获取变量。 我们还可以在调用dots = NULL时设置get_args_for,以免传递所有变量, 但是将省略号留空。

尽管如此,它并不完全健壮, 因为dots仅附加在末尾, 如果某些参数未命名, 他们最终可能会与职位相匹配。 另外,如果呼叫中的某些值应为NULL, 很难检测到它。


  

我强烈建议您不要在R包内使用以下内容。   不仅会很丑,   您会从R的CMD检查中获得关于未定义的全局变量的大量注释。

其他选项。

f_a <- function() {
    return(b + c)
}

f_e <- function() {
    b <- 2
    c <- 2
    # replace f_a's enclosing environment with the current evaluation's environment
    environment(f_a) <- environment()
    d <- f_a()
    d
}

> f_e()
[1] 4

在R包中,上述类似内容可能无法正常工作, 因为我认为程序包的功能已锁定其封闭环境。

或者:

f_a <- function() {
    with(parent.frame(), {
        b + c
    })
}

f_e <- function() {
    b <- 2
    c <- 2
    f_a()
}

> f_e()
[1] 4

这样一来,您就不会永久修改另一个函数的封闭环境。 但是,这两个功能将共享一个环境, 这样可能会发生:

f_a <- function() {
    with(parent.frame(), {
        b <- b + 1
        b + c
    })
}

f_e <- function() {
    b <- 2
    c <- 2
    d <- f_a()
    c(b,d)
}

> f_e()
[1] 3 5

在调用内部函数时会修改外部环境中的值。

还有一个更灵活的选择, 因为它仅使用eval临时修改了封闭环境。 但是,某些R函数可以通过“ daRk magic”检测其当前执行环境, 并且不能被eval欺骗; 参见this discussion

f_a <- function() {
    b <- b + 1
    b + c
}

f_e <- function() {
    b <- 2
    c <- 2
    # use current environment as enclosing environment for f_a's evaluation
    d <- eval(body(f_a), list(), enclos=environment())
    c(b=b, d=d)
}

> f_e()
b d 
2 5 

答案 1 :(得分:3)

一种选择是从调用环境中显式获取ab

f_a <- function(){
    get('b', envir = parent.frame()) + get('c', envir = parent.frame())
}

f_e <- function(){
    b = 2
    c = 2
    d = f_a()
    d
}

f_e()
#> [1] 4

或者,您可以使用quote来延迟评估,然后使用eval来评估调用环境中的代码,有效地执行相同的操作:

f_a <- function(){
    eval(quote(b + c), parent.frame())
}

但是,这实际上不是一种强大的代码编写方法,因为它限制了成功调用f_a的可能方法。遵循显式传递变量的代码要容易得多。

答案 2 :(得分:2)

编辑:

@alistaire建议使用quote来构造表达式,这提出了似乎更丑陋的另一种选择:

expr_env <- new.env()
   expr_env$f_a <- quote(b+c)
   expr_env$f_z <- quote(x+y)

f_e<-function(){
    b=2
    c=2
    d=eval( expr_env$f_a)
    print(d)
}

是否可以使用local定义函数?

 f_e<-function(){
     b=2
     c=2
     d<-local({
          b+c
              })

     print(d)
 }
 f_e()
[1] 4

另一种选择是只返回一个分析树,然后在函数的“本地”环境中完成评估。对我来说这似乎“丑陋”:

expr_list<-function(){  f_a <- quote(b+c)
                        f_z <- quote(x+y)
list(f_a=f_a,f_z=f_z) }

f_e<-function(){
    b=2
    c=2
    d=eval( (expr_list()$f_a))
    print(d)
}

答案 3 :(得分:0)

您可以将变量分配给全局环境并在内部函数中使用。

f_a <- function(){
    return(b + c)
}

f_e <- function(){
    assign("b", 2, envir = .GlobalEnv)
    assign("c", 2, envir = .GlobalEnv)
    d = f_a()
    print(d)
}

# > f_e()
# [1] 4