在这个 R 问题中,词法作用域用于实现有状态函数。点“类”的 setter 和 getter 方法按预期工作,以更改点(“类变量”)的坐标:
point <- function(x, y){
structure(class = "point", list(
x = x,
y = y,
get_x = function() paste("(", x,",",y,")"),
set_x = function(x, y){
x <<- x
y <<- y
}
))
}
x <- 0
y <- 1
p <- point(0,1)
p$get_x()
#[1] "( 0 , 1 )"
p$set_x(6,5)
p$get_x()
#[1] "( 6 , 5 )"
x
#[1] 0
y
#[1] 1
但是(正如注释所指出的)
p
引用的坐标变量看起来是一样的,即使在调用设置器之后也是如此:
> p
$x
[1] 0
$y
[1] 1
$get_x
function() paste('(', x,',',y,')')
<environment: 0x557c984f9a48>
$set_x
function(x, y){
x <<- x
y <<- y
}
<environment: 0x557c984f9a48>
attr(,"class")
[1] "point"
这是如何运作的? R 是否修改本地定义的 x,y 或者作为参数传入的 x,y?被修改的类变量存储在哪里?有没有办法在 R 中看到它?
get_x
和set_x
函数无法访问结构体中的x
。 当 get_x
尝试访问 x
时,它无法在 get_x
中找到它,因此它会查找定义 get_x
的词法环境,这是调用 point
时创建的 point
的运行时环境在那里它找到了参数x
。 当 set_x
运行时,它会尝试在同一个词法环境中设置 x
,并在那里设置 x
,而不是在结构中。 结构未修改。
get_x
和set_x
访问的变量可以这样列出:
p <- point(0,1)
ls(environment(p$set))
## [1] "x" "y"
结构中的
x
和 y
是根据 point
的参数设置的,此后它们永远不会改变。
如果再次调用
point
,不会对第一次调用 point
创建的运行时环境产生任何影响,因为它将创建一个新的独立运行时环境。
如果我们使用显式环境重写它,也许更容易理解。 这相当于问题中的版本。
point2 <- function(x, y){
e <- environment()
structure(class = "point", list(
x = x,
y = y,
get_x = function() paste("(", e$x, ",", e$y, ")"),
set_x = function(x, y){
e$x <- x
e$y <- y
}
))
}
为此,请使用环境而不是列表。这类似,但使用通过调用
point3
创建的运行时环境。
point3 <- function(x, y) {
get_x <- function() x
set_x = function(x, y) { x <<- x; y <<- y }
structure(environment(), class = "point")
}
p <- point3(0, 1)
str(mget(ls(p), p))
## List of 4
## $ get_x:function ()
## ..- attr(*, "srcref")= 'srcref' int [1:8] 2 16 2 27 16 27 2 2
## .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x0000028d7599c8b8>
## $ set_x:function (x, y)
## ..- attr(*, "srcref")= 'srcref' int [1:8] 3 15 3 49 15 49 3 3
## .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x0000028d7599c8b8>
## $ x : num 0
## $ y : num 1
p$set_x(5, 6)
str(mget(ls(p), p))
## List of 4
## $ get_x:function ()
## ..- attr(*, "srcref")= 'srcref' int [1:8] 2 16 2 27 16 27 2 2
## .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x0000028d7599c8b8>
## $ set_x:function (x, y)
## ..- attr(*, "srcref")= 'srcref' int [1:8] 3 15 3 49 15 49 3 3
## .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x0000028d7599c8b8>
## $ x : num 5
## $ y : num 6