Частные члены в референтном классе R
Возможно ли иметь закрытые поля-члены внутри ссылочного класса R. Играя с некоторыми из онлайн примеров, которые у меня есть:
> Account <- setRefClass( "ref_Account"
> , fields = list(
> number = "character"
> , balance ="numeric")
> , methods = list(
> deposit <- function(amount) {
> if(amount < 0) {
> stop("deposits must be positive")
> }
> balance <<- balance + amount
> }
> , withdraw <- function(amount) {
> if(amount < 0) {
> stop("withdrawls must be positive")
> }
> balance <<- balance - amount
> }
> ) )
>
>
> tb <- Account$new(balance=50.75, number="baml-029873") tb$balance
> tb$balance <- 12
> tb$balance
Я ненавижу тот факт, что могу обновить баланс напрямую. Возможно, что старый чистый ОО во мне, я действительно хотел бы иметь возможность сделать баланс приватным, по крайней мере, не устанавливаемым извне класса.
мысли
4 ответа
Этот ответ не работает с R > 3,00, поэтому не используйте его!
Как уже упоминалось, у вас не может быть закрытых полей. Однако, если вы используете метод инициализации, баланс не будет отображаться как поле. Например,
Account = setRefClass("ref_Account",
fields = list(number = "character"),
methods = list(
initialize = function(balance, number) {
.self$number = number
.self$balance = balance
})
Как и прежде, мы создадим экземпляр:
tb <- Account$new(balance=50.75, number="baml-0029873")
##No balance
tb
Reference class object of class "ref_Account"
Field "number":
[1] "baml-0029873"
Как я уже говорил, это не совсем приватно, так как вы все еще можете сделать:
R> tb$balance
[1] 50.75
R> tb$balance = 12
R> tb$balance
[1] 12
Для решения проблемы конфиденциальности я создаю собственный класс "Private", в котором есть новые методы доступа к объекту, т.е. $
а также [[
, Эти методы выдают ошибку, если клиент пытается получить доступ к "приватному" члену. Частный член идентифицируется по имени (ведущий период). Поскольку ссылочные объекты являются средами в R, можно обойти это, но сейчас это мое решение, и я считаю более удобным использовать методы get/set, предоставляемые классом. Так что это скорее решение вопроса "трудно установить извне".
Я организовал это в R-пакете, так что следующий код использует этот пакет и модифицирует приведенный выше пример так, чтобы присвоение tb$.balance
выдает ошибку. Я также использую функцию Class
который является просто оберткой вокруг setRefClass
так что это все еще входит в область действия ссылочных классов R, предоставляемых пакетом методов и используемых в вопросе.
devtools::install_github("wahani/aoos")
library("aoos")
Account <- defineRefClass({
Class <- "Account"
contains <- "Private"
number <- "character"
.balance <- "numeric"
deposit <- function(amount) {
if(amount < 0) stop("deposits must be positive")
.balance <<- .balance + amount
}
withdraw <- function(amount) {
if(amount < 0) stop("withdrawls must be positive")
.balance <<- .balance - amount
}
})
tb <- Account(.balance = 50.75, number = "baml-029873")
tb$.balance # error
tb$.balance <- 12 # error
Я столкнулся с подобной проблемой и реализовал ее таким образом, используя Base R. Я склонен усложнять себе жизнь, не используя сторонние пакеты, такие как R6. Чтобы решить эту проблему, я получаю доступ к среде, в которой определены методы объекта, и таким образом храню переменные.
В этом примере я пытаюсь реализовать MinMaxScaler, подобный тому, который можно найти в scikit learn:
## Base reference class
setRefClass(
"Transformer",
contains = "VIRTUAL",
methods = list(
fit = function(data) stop("Must implement"),
transform = function(data) stop("Must implement"),
fit_transform = function(data) {
fit(data)
transform(data)
}
))
Конкретная реализация Transformer API. в fit
метод, я получаю доступ к среде, где fit
определено. Затем я использую эту среду для хранения любых переменных, которые мне нужны для промежуточных вычислений, и для обновления объекта на месте - как в sklearn.
MinMaxScaler <-setRefClass(
"MinMaxScaler",
contains = "Transformer",
fields = c(feature_range = "numeric"),
methods = list(
fit = function(data) {
env <- environment(fun = .self$fit)
rng <- range(data, na.rm=TRUE)
env$data_range_ <- diff(range(data, na.rm=TRUE))
env$data_min_ <- rng[[1]]
env$data_max_ <- rng[[2]]
},
transform = function(data) {
env <- environment(fun = .self$transform)
scalef <- diff(range(feature_range))
scalef * (data - env$data_min_) / env$data_range_ + min(feature_range)
}
)
)
Чтобы продемонстрировать этот паттерн, я создам два скейлера и подгону их отдельно:
> ## Dummy data
> set.seed(123)
> z <- rnorm(1e4)
> summary(z)
Min. 1st Qu. Median Mean 3rd Qu. Max.
-3.845320 -0.667969 -0.011089 -0.002372 0.673347 3.847768
>
> scaler1 <- MinMaxScaler(feature_range=c(0, 50))
> summary(scaler1$fit_transform(z))
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00 20.65 24.92 24.98 29.37 50.00
>
> scaler2 <- MinMaxScaler(feature_range=c(-100, 100))
> summary(scaler2$fit_transform(z))
Min. 1st Qu. Median Mean 3rd Qu. Max.
-100.00000 -17.39725 -0.32011 -0.09347 17.47344 100.00000
>
> ## to show the scalers are distinct and not sharing private vars
> summary(scaler1$transform(z))
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00 20.65 24.92 24.98 29.37 50.00
> summary(scaler2$transform(z))
Min. 1st Qu. Median Mean 3rd Qu. Max.
-100.00000 -17.39725 -0.32011 -0.09347 17.47344 100.00000