这可能不是正确的术语,但希望我可以理解我的观点。
我经常最终做类似的事情:
myVar = 1
f <- function(myvar) { return(myVar); }
# f(2) = 1 now
R愉快地使用了函数范围之外的变量,这让我摸不着头脑,想知道我怎么可能得到我的结果。
是否有任何选项说“强迫我只使用之前已在此功能范围内指定值的变量”?例如,Perl的use strict
做了类似的事情。但我不知道R有相当于my
。
我想知道当我这样做时R是否可以自动警告我。
编辑2:另外,如果Rkward或其他IDE提供此功能,我也想知道。
答案 0 :(得分:26)
据我所知,R没有提供“使用严格”模式。所以你有两个选择:
1 - 确保所有“严格”功能都没有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
答案 1 :(得分:12)
checkUsage
包中的 codetools
会有所帮助,但不会让你一路走来。
在未定义myVar
的干净会话中,
f <- function(myvar) { return(myVar); }
codetools::checkUsage(f)
给出
<anonymous>: no visible binding for global variable ‘myVar’
但是一旦定义myVar
,checkUsage
就会很高兴。
请参阅?codetools
包中的codetools
:这可能是有用的:
> findGlobals(f)
[1] "{" "myVar" "return"
> findLocals(f)
character(0)
答案 2 :(得分:8)
使用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
答案 3 :(得分:7)
你当然做错了。不要指望静态代码检查工具能够找到你所有的错误。通过测试检查您的代码。还有更多的测试。编写在干净环境中运行的任何体面测试都会发现这种错误。为您的函数编写测试,并使用它们。看看CRAN上的testthat包的荣耀。
答案 4 :(得分:7)
CRAN上有一个新的包.floating-box {
float: left;
width: 150px;
height: 75px;
margin: 10px;
border: 3px solid #73AD21;
}
来解决这个常见问题(请参阅插图here)。使用modules
时,该函数会引发错误,而不是以静默方式返回错误的结果。
modules
这是我第一次使用它。它似乎很简单,所以我可以将它包含在我的常规工作流程中,以防止出现耗时的事故。
答案 5 :(得分:6)
您需要修正拼写错误:myvar
!= myVar
。那一切都会奏效......
范围分辨率是“由内而外”从当前开始,然后是封闭等等。
修改现在你澄清了你的问题,看一下包codetools(它是R Base集的一部分):
R> library(codetools)
R> f <- function(myVAR) { return(myvar) }
R> checkUsage(f)
<anonymous>: no visible binding for global variable 'myvar'
R>
答案 6 :(得分:4)
您可以像这样动态更改环境树:
a <- 1
f <- function(){
b <- 1
print(b)
print(a)
}
environment(f) <- new.env(parent = baseenv())
f()
在f
内,可以找到b
,而a
则无法找到。{/ p>
但可能弊大于利。
答案 7 :(得分:3)
您可以测试以查看变量是否在本地定义:
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函数在特定环境中搜索变量名称。 inherits = FALSE告诉它不要查看封闭的框架。
答案 8 :(得分:3)
environment(fun) = parent.env(environment(fun))
将从搜索路径中删除“工作区”,保留其他所有内容。这可能与你想要的最接近。
答案 9 :(得分:2)
@Tommy gave a very good answer and I used it to create 3 functions that I think are more convenient in practice.
strict
to make a function strict, you just have to call
strict(f,x,y)
instead of
f(x,y)
example:
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
To get a diagnosis, execute checkStrict1(f) with optional Boolean parameters to show more ore less.
checkStrict1("my_fun1") # nothing
checkStrict1("my_fun2") # my_fun2 : B
A more complicated case:
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
I chose to show only inexistent by default out of the 3 optional additions. You can change it easily in the function definition.
checkStrictAll
Get a diagnostic of all your potentially problematic functions, with the same parameters.
checkStrictAll()
my_fun2 : B
my_fun3 : E
my_fun3 Inexistent : H
sources
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))
}
答案 10 :(得分:1)
根据@ c-urchin的回答,对我有用的是定义一个脚本,该脚本读取我的所有功能,然后排除全局环境:
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='')))
}
我认为
./fun
和.R
文件只包含一个与文件名称相同的函数。问题是,如果我的一个函数调用了我的另一个函数,那么外部函数也必须首先调用此脚本,并且必须使用local=T
调用它:
source('readfun.R', local=T)
当然假设脚本文件名为readfun.R
。