В этом вопросе R лексическая область видимости используется для реализации функции с отслеживанием состояния. Методы установки и получения для «класса» точки работают, как и ожидалось, изменяя координаты точки, «переменной класса»:
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 или те, которые переданы в качестве параметров? Где хранится измененная переменная класса? Есть ли способ увидеть это в 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(eapply(p, c))
## 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(eapply(p, c))
## 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
Альтернативно используйте список, как в вопросе, но не включайте x
и y
в структуру, а просто полагайтесь на x
и y
в point
, что он и делает в любом случае. Подробнее об этом забеге
demo(scoping)