R力局部范围

这可能是不正确的术语,但希望我能明白我的观点。

我经常最终做一些事情:

myVar = 1 f <- function(myvar) { return(myVar); } # f(2) = 1 now 

R愉快地使用了函数范围之外的variables,这让我挠脑袋,想知道我怎么能得到我的结果。

是否有任何选项说:“强迫我只使用以前在此function的范围内赋值的variables”? 例如,Perl的use strict就是这样的。 但是我不知道R与my的相同。


编辑:谢谢,我知道我把它们大写的不同。 的确,这个例子是专门为了说明这个问题而创build的!

我想知道当我这样做的时候,R可以自动提醒我的方法。

编辑2:另外,如果Rkward或其他IDE提供这种function,我也想知道这一点。

据我所知,R不提供“严格使用”模式。 所以你有两个select:

1 – 确保所有“严格”function都没有globalenv作为环境。 你可以为此定义一个很好的包装函数,但最简单的方法是调用local

 # Use "local" directly to control the function environment f <- local( function(myvar) { return(myVar); }, as.environment(2)) f(3) # Error in f(3) : object 'myVar' not found # Create a wrapper function "strict" to do it for you... strict <- function(f, pos=2) eval(substitute(f), as.environment(pos)) f <- strict( function(myvar) { return(myVar); } ) f(3) # Error in f(3) : object 'myVar' not found 

2 – 做一个代码分析,警告你“不好”的用法。

这是一个函数checkStrict ,希望能做到你想要的。 它使用优秀的codetools包。

 # Checks a function for use of global variables # Returns TRUE if ok, FALSE if globals were found. checkStrict <- function(f, silent=FALSE) { vars <- codetools::findGlobals(f) found <- !vapply(vars, exists, logical(1), envir=as.environment(2)) if (!silent && any(found)) { warning("global variables used: ", paste(names(found)[found], collapse=', ')) return(invisible(FALSE)) } !any(found) } 

并尝试一下:

 > myVar = 1 > f <- function(myvar) { return(myVar); } > checkStrict(f) Warning message: In checkStrict(f) : global variables used: myVar 

checkUsage包中的codetools是有帮助的,但并不能让你codetools 。 在未定义myVar的清理会话中,

 f <- function(myvar) { return(myVar); } codetools::checkUsage(f) 

 <anonymous>: no visible binding for global variable 'myVar' 

但是一旦你定义了myVarcheckUsage就开心了。

请参阅codetools包中的codetools :有可能有用:

 > findGlobals(f) [1] "{" "myVar" "return" > findLocals(f) character(0) 

使用get(x, inherits=FALSE)将强制本地作用域。

  myVar = 1 f2 <- function(myvar) get("myVar", inherits=FALSE) f3 <- function(myvar){ myVar <- myvar get("myVar", inherits=FALSE) } 

输出:

 > f2(8) Error in get("myVar", inherits = FALSE) : object 'myVar' not found > f3(8) [1] 8 

你需要修正错字: myvar != myVar 。 那么这一切将工作…

范围决议是从现在开始,然后是封闭等等。

编辑现在你已经澄清了你的问题,看看包的代码工具(它是R Base集的一部分):

 R> library(codetools) R> f <- function(myVAR) { return(myvar) } R> checkUsage(f) <anonymous>: no visible binding for global variable 'myvar' R> 

你当然是做错了。 不要期望静态代码检查工具find你所有的错误。 用testing检查你的代码。 还有更多的testing。 任何写在一个干净的环境运行的体面testing将发现这种错误。 为您的function编写testing,并使用它们。 看看CRAN上的testing包的荣耀。

CRAN上有一个新的封装modules来解决这个常见的问题(参见这里的小插图)。 使用modules ,该函数会引发错误,而不是默默地返回错误的结果。

 # without modules myVar <- 1 f <- function(myvar) { return(myVar) } f(2) [1] 1 # with modules library(modules) m <- module({ f <- function(myvar) { return(myVar) } }) m$f(2) Error in m$f(2) : object 'myVar' not found 

这是我第一次使用它。 这似乎是直截了当的,所以我可能会将其纳入我的常规工作stream程,以防止耗时的事故。

你可以像这样dynamic地改变环境树:

 a <- 1 f <- function(){ b <- 1 print(b) print(a) } environment(f) <- new.env(parent = baseenv()) f() 

f里面,可以findb ,而不能。

但可能会造成更多的伤害,而不是好的。

您可以testing以查看variables是否在本地定义:

 myVar = 1 f <- function(myvar) { if( exists('myVar', environment(), inherits = FALSE) ) return( myVar) else cat("myVar was not found locally\n") } > f(2) myVar was not found locally 

但是,如果你唯一要做的就是保护自己免于拼写错误,我觉得这是非常虚假的。

exists函数在特定环境中searchvariables名称。 inherits = FALSE指示不要查看封闭的框架。

environment(fun) = parent.env(environment(fun))

将从您的searchpath中删除“工作区”,保留其他所有内容。 这可能是最接近你想要的。

基于@ c-urchin的回答,对我而言,我的定义是一个脚本,它可以读取我所有的function,然后排除全球环境:

 filenames <- Sys.glob('fun/*.R') for (filename in filenames) { source(filename, local=T) funname <- sub('^fun/(.*).R$', "\\1", filename) eval(parse(text=paste('environment(',funname,') <- parent.env(globalenv())',sep=''))) } 

我假设

  • 所有的function,没有别的都包含在相对目录./fun
  • 每个.R文件只包含一个与文件名称相同的函数。

问题是,如果我的一个函数调用了另外一个函数,那么外部函数也必须先调用这个脚本,而且必须用local=T来调用它:

 source('readfun.R', local=T) 

假设脚本文件被称为readfun.R

@Tommy给出了很好的答案,我用它来创build3个function,我认为在实践中更方便。

严格

使function严格,你只需要打电话

 strict(f,x,y) 

代替

 f(x,y) 

例:

 my_fun1 <- function(a,b,c){a+b+c} my_fun2 <- function(a,b,c){a+B+c} B <- 1 my_fun1(1,2,3) # 6 strict(my_fun1,1,2,3) # 6 my_fun2(1,2,3) # 5 strict(my_fun2,1,2,3) # Error in (function (a, b, c) : object 'B' not found 

checkStrict1

为了得到一个诊断,执行带有可选布尔参数的checkStrict1(f)来显示更多的信息。

 checkStrict1("my_fun1") # nothing checkStrict1("my_fun2") # my_fun2 : B 

更复杂的情况:

 A <- 1 # unambiguous variable defined OUTSIDE AND INSIDE my_fun3 # B unambiguous variable defined only INSIDE my_fun3 C <- 1 # defined OUTSIDE AND INSIDE with ambiguous name (C is also a base function) D <- 1 # defined only OUTSIDE my_fun3 (D is also a base function) E <- 1 # unambiguous variable defined only OUTSIDE my_fun3 # G unambiguous variable defined only INSIDE my_fun3 # H is undeclared and doesn't exist at all # I is undeclared (though I is also base function) # v defined only INSIDE (v is also a base function) my_fun3 <- function(a,b,c){ A<-1;B<-1;C<-1;G<-1 a+b+A+B+C+D+E+G+H+I+v+ my_fun1(1,2,3) } checkStrict1("my_fun3",show_global_functions = TRUE ,show_ambiguous = TRUE , show_inexistent = TRUE) # my_fun3 : E # my_fun3 Ambiguous : D # my_fun3 Inexistent : H # my_fun3 Global functions : my_fun1 

我select了只显示不存在的3个可选的添加默认情况下。 您可以在函数定义中轻松更改它。

checkStrictAll

使用相同的参数获取所有潜在问题function的诊断。

 checkStrictAll() my_fun2 : B my_fun3 : E my_fun3 Inexistent : H 

来源

 strict <- function(f1,...){ function_text <- deparse(f1) function_text <- paste(function_text[1],function_text[2],paste(function_text[c(-1,-2,-length(function_text))],collapse=";"),"}",collapse="") strict0 <- function(f1, pos=2) eval(substitute(f1), as.environment(pos)) f1 <- eval(parse(text=paste0("strict0(",function_text,")"))) do.call(f1,list(...)) } checkStrict1 <- function(f_str,exceptions = NULL,n_char = nchar(f_str),show_global_functions = FALSE,show_ambiguous = FALSE, show_inexistent = TRUE){ functions <- c(lsf.str(envir=globalenv())) f <- try(eval(parse(text=f_str)),silent=TRUE) if(inherits(f, "try-error")) {return(NULL)} vars <- codetools::findGlobals(f) vars <- vars[!vars %in% exceptions] global_functions <- vars %in% functions in_global_env <- vapply(vars, exists, logical(1), envir=globalenv()) in_local_env <- vapply(vars, exists, logical(1), envir=as.environment(2)) in_global_env_but_not_function <- rep(FALSE,length(vars)) for (my_mode in c("logical", "integer", "double", "complex", "character", "raw","list", "NULL")){ in_global_env_but_not_function <- in_global_env_but_not_function | vapply(vars, exists, logical(1), envir=globalenv(),mode = my_mode) } found <- in_global_env_but_not_function & !in_local_env ambiguous <- in_global_env_but_not_function & in_local_env inexistent <- (!in_local_env) & (!in_global_env) if(typeof(f)=="closure"){ if(any(found)) {cat(paste(f_str,paste(rep(" ",n_char-nchar(f_str)),collapse=""),":", paste(names(found)[found], collapse=', '),"\n"))} if(show_ambiguous & any(ambiguous)) {cat(paste(f_str,paste(rep(" ",n_char-nchar(f_str)),collapse=""),"Ambiguous :", paste(names(found)[ambiguous], collapse=', '),"\n"))} if(show_inexistent & any(inexistent)) {cat(paste(f_str,paste(rep(" ",n_char-nchar(f_str)),collapse=""),"Inexistent :", paste(names(found)[inexistent], collapse=', '),"\n"))} if(show_global_functions & any(global_functions)){cat(paste(f_str,paste(rep(" ",n_char-nchar(f_str)),collapse=""),"Global functions :", paste(names(found)[global_functions], collapse=', '),"\n"))} return(invisible(FALSE)) } else {return(invisible(TRUE))} } checkStrictAll <- function(exceptions = NULL,show_global_functions = FALSE,show_ambiguous = FALSE, show_inexistent = TRUE){ functions <- c(lsf.str(envir=globalenv())) n_char <- max(nchar(functions)) invisible(sapply(functions,checkStrict1,exceptions,n_char = n_char,show_global_functions,show_ambiguous, show_inexistent)) }