В этом вопросе 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)