R 作用域:函数中不允许使用全局变量

R scoping: disallow global variables in function

如果在 R 函数中使用全局变量,是否有任何方法可以发出警告(并失败...)?我认为这样可以节省很多并防止意外行为......例如

sUm <- 10
sum <- function(x,y){
sum = x+y
return(sUm)
}

由于 return 中的 "typo",函数将始终 return 10。它应该失败,而不是 returning sUm 的值。

使用get是一种方式:

sUm <- 10
sum <- function(x,y){
  sum <- x+y
  #with inherits = FALSE below the variable is only searched 
  #in the specified environment in the envir argument below
  get('sUm', envir = environment(), inherits=FALSE) 
}

输出:

> sum(1,6)
Error in get("sUm", envir = environment(), inherits = FALSE) : 
  object 'sUm' not found

在 get 函数中拥有正确的 sum 仍然只会在函数的环境中查找变量,这意味着如果有两个变量,一个在函数内部,一个在全局环境中,具有相同的变量name,该函数将始终在函数环境中查找变量,而不会在全局环境中查找:

sum <- 10
sum2 <- function(x,y){
  sum <- x+y
  get('sum', envir = environment(), inherits=FALSE) 
}

> sum2(1,7)
[1] 8

您可以检查变量名是否出现在全局变量列表中。请注意,如果所讨论的全局变量与函数的参数同名,这是不完美的。

if (deparse(substitute(var)) %in% ls(envir=.GlobalEnv))
    stop("Do not use a global variable!")

stop() 函数将停止函数的执行并显示给定的错误消息。

无法永久更改变量的解析方式,因为这会破坏很多功能。你不喜欢的行为在很多时候其实很有用

如果在函数中找不到变量,R 将检查为此类变量定义函数的环境。您可以使用 environment() 函数更改此环境。例如

environment(sum) <- baseenv()
sum(4,5)
# Error in sum(4, 5) : object 'sUm' not found

这是有效的,因为 baseenv() 指向空的 "base" 环境。但是,请注意,您无法使用此方法访问其他功能

myfun<-function(x,y) {x+y}
sum <- function(x,y){sum = myfun(x+y); return(sUm)}

environment(sum)<-baseenv()
sum(4,5)
# Error in sum(4, 5) : could not find function "myfun"

因为在 R 等函数式语言中,函数只是常规变量,它们也在定义它们的环境中起作用,并且在基础环境中不可用。

您必须手动更改您编写的每个函数的环境。同样,无法更改此默认行为,因为包中定义的许多基本 R 函数和函数都依赖于此行为。

另一种方式(或风格)是将所有全局变量保存在一个特殊的环境中:

with( globals <- new.env(), {
  # here define all "global variables"  
  sUm <- 10
  mEan <- 5
})

# or add a variable by using $
globals$another_one <- 42

那么函数将无法获取它们:

sum <- function(x,y){
  sum = x+y
  return(sUm)
}

sum(1,2)
# Error in sum(1, 2) : object 'sUm' not found

但您始终可以将它们与全局变量一起使用$:

globals$sUm
[1] 10

要管理纪律,可以检查globals之外是否有任何全局变量(函数除外):

setdiff(ls(), union(lsf.str(), "globals")))

我的另一个答案更多是关于您可以在函数内部采用什么方法。现在,我将提供一些关于定义函数后要做什么的见解。

为确保您的函数在不该使用的情况下不使用全局变量,请使用 codetools 程序包。

library(codetools)

sUm <- 10
f <- function(x, y) {
    sum = x + y
    return(sUm)
}

checkUsage(f)

这将打印消息:

<anonymous> local variable ‘sum’ assigned but may not be used (:1)

要查看您的函数中是否使用了任何全局变量,您可以将 findGlobals() 函数的输出与全局环境中的变量进行比较。

> findGlobals(f)
[1] "{"  "+"  "="  "return"  "sUm"

> intersect(findGlobals(f), ls(envir=.GlobalEnv))
[1] "sUm"

这告诉您全局变量 sUmf() 中被使用,而它可能不应该被使用。