【问题标题】:Private Members in R Reference ClassR 引用类中的私有成员
【发布时间】:2011-12-30 20:28:45
【问题描述】:

是否可以在 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

我讨厌我可以直接更新余额。也许是我身上的旧纯 OO,我真的希望能够将平衡设为私有,至少不能从课堂外设置。

想法

【问题讨论】:

  • R6 包/框架内置了私有字段和方法(据称性能更高)。

标签: r reference-class


【解决方案1】:

为了解决隐私问题,我创建了一个自己的类“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

【讨论】:

    【解决方案2】:

    这个答案不适用于 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
    

    【讨论】:

    • 为什么这不适用于 R > 3.00?你现在可以原生地做私人会员吗?
    【解决方案3】:

    我遇到了一个类似的问题,并使用 Base R 以这种方式实现它。我倾向于不使用 R6 等第三方软件包,从而使事情变得更加困难。为了解决这个问题,我访问了定义对象方法的环境并以这种方式存储变量。

    在这个例子中,我试图实现一个像 scikit learn 中那样的 MinMaxScaler:

    ## 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 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-16
      • 1970-01-01
      • 1970-01-01
      • 2011-02-24
      • 2017-12-22
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多