R: 如何理解变量和环境的Lexical Scoping Rule

来源:互联网 发布:淘宝品质保证险是什么 编辑:程序博客网 时间:2024/06/18 15:52

非IT出身,看到Scoping Rule神马的是非常头疼的,尤其是需要涉及到Function的嵌套的时候,很容易将不同environment种的变量弄混淆,还是自己动手实践一下比较好。
在一个Function中,对一个变量的lexical scoping rule的检索次序是,先检查本Function中的environment,如果能找到该变量,则返回该变量,如果不能,则检索这个Function被创建(不是被调用)的Environment,以此类推,一直到检索Global Environment。

写了2段code,在写code之前,先提出两个问题:
1. a和全局环境各存在一个环境e,当a调用b时,b中的e$x, e$y和e$z如何检索?
2. 全局环境变量的赋值符号 "<<-"如何发挥作用?

第一段:b函数在a函数创建,a函数及global env.各创建一个环境变量e

# Chunk 1: 
> a <- function(x){
    e <- new.env()
    e$x <- 111
   
    c <- b(x)
    return(c(c, e$y, e$z))
}

b <- function(x){
    x <- e$x + x
    e$y <- x + 1
    e$z <<- x + 2
    return(x)
}

e <- new.env()
e$x <- 222


a(5)
# [1] 227
e$y
# [1] 228
e$z
# [1] 229

运行完这一段,我们可以回答上面提出的两个问题:
1. a 中的环境e及其中的变量x (=111)被忽略,Function b调用的e环境,首先在b里进行检索,无果,然后进入b被创建的环境(即与Function a,和另一个Environment e被同时创建的环境,暂且理解为Global Env.)进行检索,这里的e中,x = 222,所以a(5)返回的并不是111+5 而是 222+5,另a中的e$y, e$z均为NULL.
2. b函数中的x = e$x + x = 227,与此同时将更新后的x,加上1和2,分别赋值给e环境中的y和z,e的检索方式和答案1中的逻辑一致,均赋值给Global Env.中的e环境,不管赋值符是什么形式的,注意这里只有两层环境,无法做更多的检验,所以我们要进一步检查“<<-”的作用。

rm(list = ls())之后(清空刚才创建的所有环境和变量),再来看第二段code:
b函数在a函数创建,a函数及global env.各创建一个环境变量e

# Chunk 2:
a <- function(x){
    e <- new.env()
    e$x <- 111
    
    b <- function(x){
        x <- e$x + x
        e$y <- x + 1
        e$z <<- x + 2
        return(x)
    }
    
    c <- b(x)
    return(c(c, e$y, e$z))
}

e <- new.env()
e$x <- 222

a(5)
# [1] 116 117 118

e$z
# NULL

接下来回答同样的问题:
1. b函数创建的环境中同时创建了一个环境变量e,尽管a创建的环境中也有一个e,但是b函数里调用的e$x显然来自前者。所以这段code中a(5)可以返回3个数值,因为这三个数的环境e均来自于a函数的环境。
2. 在global env.中调用e$z并没有返回118,而是返回的NULL,说明"<<-"赋值符并不是直接赋值给Global Env,而是首先在b的母环境,即创建的环境中检索,一旦检索到母环境有环境变量e,便立即赋值给这个环境变量中的对应变量值。

那么,来个简单的小结:
1. 对于函数内部变量的检索,如果在函数内部环境中无法找到,则到函数创建环境中检索,依次类推,知道Global Env.;
2. 对于"<-"函数内部变量的赋值符,检索规则与函数内部变量调用时的检索的规则无异;
3. 对于"<<-"函数变量的赋值符,检索时跳过函数内部环境,直接检索函数创建环境,直到找到变量为止,如果找不到,则赋值给Global Env.,如果变量前有环境变量限制,如env$var,如果Global Env.里也没有这个环境变量"env",则无法完成赋值。

0 0