R中的全局变量和局部变量

154

我是R语言的新手,对于R语言中本地变量和全局变量的使用感到困惑。

我在网上读到一些帖子说,如果我使用 =<-,那么我将在当前环境中分配变量,并且使用 <<- 可以在函数内部访问全局变量。

然而,就我所记得的C++来看,当你在括号 {} 中声明一个变量时,会产生一个局部变量,那么我想知道这个在R中是否也是一样的呢?或者仅仅对于R中的函数才有局部变量的概念。

我进行了一个小实验,似乎只有括号还不够,我是否有什么地方理解错误呢?

{
   x=matrix(1:10,2,5)
}
print(x[2,2])
[1] 4

除了这些答案之外,还有一些要运行的代码:globalenv(); globalenv() %>% parent.env; globalenv() %>% parent.env %>% parent.env,... - isomorphismes
@isomorphismes,错误:找不到函数“%>%”。那是另一种赋值形式吗? - Aaron McDaid
1
R-help上的相关帖子:"<<-"运算符是什么意思? - Henrik
2
@AaronMcDaid 你好,抱歉没有更早回复!那是来自 require(magrittr)。这是一种在右侧应用函数(x | f1 | f2 | f3)而不是在左侧的方法(f3(f2(f1(x))))。 - isomorphismes
3个回答

180

函数内声明的变量作用域仅限于该函数内。例如:

foo <- function() {
    bar <- 1
}
foo()
bar

出现以下错误:Error: object 'bar' not found

如果你想让 bar 成为全局变量,应该这样做:

foo <- function() {
    bar <<- 1
}
foo()
bar
在这种情况下,bar 可以从函数外部访问。
然而,与C、C++或其他许多语言不同的是,花括号不确定变量的作用域。例如,在以下代码片段中:
if (x > 10) {
    y <- 0
}
else {
    y <- 1
}

if-else 语句后,y 仍然可以被访问。

正如你所说,您也可以创建嵌套环境。您可以查看这两个链接以了解如何使用它们:

  1. http://stat.ethz.ch/R-manual/R-devel/library/base/html/environment.html
  2. http://stat.ethz.ch/R-manual/R-devel/library/base/html/get.html

这里有一个小例子:

test.env <- new.env()

assign('var', 100, envir=test.env)
# or simply
test.env$var <- 100

get('var') # var cannot be found since it is not defined in this environment
get('var', envir=test.env) # now it can be found

156

<- 在当前环境中进行赋值操作。

在函数内部时,R 会为您创建一个新的环境。默认情况下,它包含从创建它的环境中继承的所有变量,因此您也可以使用这些变量,但是任何新创建的变量都不会被写入全局环境。

在大多数情况下,<<- 会向全局环境中的变量赋值或在函数内部创建一个变量并将其写入全局环境。然而,它并不完全是那么简单。它会检查父环境中是否存在感兴趣的变量名称。如果在您的父环境中找不到它,它会转到父级父级环境(在函数创建时)并在那里查找。它一直向上到全局环境,如果在全局环境中找不到它,则会将变量分配给全局环境。

这可能说明了正在发生的事情。

bar <- "global"
foo <- function(){
    bar <- "in foo"
    baz <- function(){
        bar <- "in baz - before <<-"
        bar <<- "in baz - after <<-"
        print(bar)
    }
    print(bar)
    baz()
    print(bar)
}
> bar
[1] "global"
> foo()
[1] "in foo"
[1] "in baz - before <<-"
[1] "in baz - after <<-"
> bar
[1] "global"
第一次打印bar时,我们还没有调用foo,所以它应该仍然是全局的——这很有道理。第二次打印是在调用baz之前在foo内部打印的,所以"in foo"的值是有意义的。接下来我们可以看到&​lt;&​lt;-实际上做了什么。下一个打印的值是"in baz - before <<-",即使print语句出现在<<-之后。这是因为&​lt;&​lt;-不会查找当前环境(除非您在全局环境中,在这种情况下&​lt;&​lt;-的作用类似于&​lt;-)。因此,在baz内部,bar的值保持为"in baz - before <<-"。一旦我们调用了baz,foo内部的bar副本就会被更改为"in baz",但正如我们所看到的,全局的bar没有改变。这是因为定义在foo内部的bar副本在创建baz时位于父环境中,因此这是&​lt;&​lt;-所看到的第一个bar副本,并因此分配给它的副本。因此,&​lt;&​lt;-不仅直接分配给全局环境。
&​lt;&​lt;-很棘手,如果可以避免,我不建议使用它。如果您真的想分配给全局环境,可以使用assign函数并明确告诉它您想要全局分配。
现在我将&​lt;&​lt;-更改为分配语句,我们可以看到其效果:
bar <- "global"
foo <- function(){
    bar <- "in foo"   
    baz <- function(){
        assign("bar", "in baz", envir = .GlobalEnv)
    }
    print(bar)
    baz()
    print(bar)
}
bar
#[1] "global"
foo()
#[1] "in foo"
#[1] "in foo"
bar
#[1] "in baz"

所以两次在 foo 内部打印bar时,即使调用了baz,其值仍为“in foo”。这是因为assign从未考虑过foo内部的bar副本,因为我们告诉它确切的查找位置。但是,这一次全局环境中的bar的值发生了变化,因为我们明确地进行了赋值。

现在你还问如何创建局部变量,你也可以很容易地做到,而无需创建函数...我们只需要使用local函数。

bar <- "global"
# local will create a new environment for us to play in
local({
    bar <- "local"
    print(bar)
})
#[1] "local"
bar
#[1] "global"

我能否配置一个函数,使其内部的环境与外部相同?这意味着使用 <- 创建的任何变量都将在外部可用。 - Mojimi
有点是,但这是个糟糕的想法。我不会帮你做那个,因为它太糟糕了。最好的方法是返回结果,让用户决定如何使用它们。 - Dason

5
一些相关内容:
更进一步的内容
attrs <- {}

attrs.a <- 1

f <- function(d) {
    attrs.a <- d
}

f(20)
print(attrs.a)

将会打印出“1”

attrs <- {}

attrs.a <- 1

f <- function(d) {
   attrs.a <<- d
}

f(20)
print(attrs.a)

将打印出"20"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接