如何检测R函数中的自由变量名称

时间:2014-08-18 23:12:07

标签: r

假设我有一个功能:

f <- function() {
  x + 1
}

此处x是一个自由变量,因为它的值未在函数f中定义。 有没有办法可以从定义的函数中获取变量名称,比如x,比如f

我问这个问题,同时保持其他人的问题。旧的R代码。使用了很多自由变量,这使调试变得困难。

欢迎任何建议。

2 个答案:

答案 0 :(得分:7)

codetools包具有用于此目的的功能,例如findGlobals

findGlobals(f, merge=FALSE)[['variables']]
# [1] "x"

如果我们将函数重新定义为具有命名参数x,则不返回任何变量。

f2 <- function(x){
  x+1
}
findGlobals(f2, merge=FALSE)[['variables']]
# character(0)

答案 1 :(得分:2)

这是一个粗略的尝试。

find_vars <- function(f, vars=list(found=character(), defined=names(formals(f)))) {
    if( is.function(f) ) {
        # function, begin search on body
        return(find_vars(body(f), vars))
    } else if (is.call(f) && deparse(f[[1]]) == "<-") {
        # assignment with <- operator
        if (is.recursive(f[[2]])) {
           if (is.call(f[[2]]) && deparse(f[[2]][[1]]) == "$") {
               vars$defined <- unique( c(vars$defined, deparse(f[[2]][[1]])) )  
           } else {
               warning(paste("unable to determine assignments variable in", deparse(f[[2]])))
           }
        } else {
            vars$defined <- unique( c(vars$defined, deparse(f[[2]])) )  
        }
        vars <- find_vars(f[[3]], vars)
    } else if (is.call(f) && deparse(f[[1]]) == "$") {
        # assume "b" is ok in a$b
        vars <- find_vars(f[[2]], vars)
    } else if (is.call(f) && deparse(f[[1]]) == "~") {
        #skip formulas
    } else if (is.recursive(f)) {
        # compound object, iterate through sub-parts
        v <- lapply(as.list(f)[-1], find_vars, vars)
        vars$defined <- unique( c(vars$defined, unlist(sapply(v, `[[`, "defined"))) )
        vars$found <- unique( c(vars$found, unlist(sapply(v, `[[`, "found"))) )
    } else if (is(f, "name")) {
        # standard variable name/symbol
        vars$found <- unique( c(vars$found, deparse(f)))
    }
    vars
}

find_free <- function(f) {
    r <- find_vars(f)
    return(setdiff(r$found, r$defined))
}

然后你可以像

一样使用它
f <- function() {
  z <- x + 1
  z
}
find_free(f)
# [1] "x"

我确定有很多可能存在误报,我没有对非标准评估的功能进行任何特殊编码。例如

g <- function(df) {
  with(df, mpg + disp)
}
g(head(mtcars))
# [1] 181 181 131 279 379 243

但是

find_free(g)
# [1] "mpg"  "disp"

我已经为$运算符和公式添加了一个特殊分支;您可以为具有非标准评估的函数(如with()subset()或您喜欢的任何内容)添加特殊分支。这取决于你的代码最终看起来像什么。

这假设所有作业都是通过标准<-进行的。还有其他方法可以分配不会被检测到的变量(即assign())。我们也忽略所有函数调用。因此,如果您致电myfun(1),它就不会将myfun报告为自由变量,即使它可能是一个&#34;免费功能&#34;在代码中的其他位置定义。

所以这可能并不完美,但它应该成为解决潜在问题的合适屏幕。