【问题标题】:Use an expression to subset R6Class based on attributes使用表达式根据属性对 R6Class 进行子集化
【发布时间】:2020-07-07 17:48:22
【问题描述】:

我有一个 R6 类 BarContainer,其中包含一个向量 Bar,其中 Bar 是另一个 R6 类。我想在BarContainer 中实现一个方法subset,它将Bar 的向量子集以匹配作为参数给出的表达式。

Bar = R6::R6Class(
    ### Class name
    "Bar",

    ### Public members
    public = list(
        ### Attributes
        a = -1,
        b = -1,
        c = -1,

        ### Initialize
        initialize = function(A, B, C)
        {
            self$a = A
            self$b = B
            self$c = C
        }
    )
)

BarContainer = R6::R6Class(
    ### Class name
    "BarContainer",

    ### Public members
    public = list(
        ### Attributes
        self$bars = c(),

        ### Initialize
        initialize = function(input)
        {
            bars = input
        }#,

        ### Subset
        subset = function(expr)
        {
            # ??? self[eval(parse(text=expr))] ???
        }
    )
)

这是subset的一个可能用例

mySubsettedContainer = myContainer$subset(a == 3 && (b > 0 || c > 0) )

如何实现?任何其他解决方案(最终使用多个表达式或其他)来实现这个subset 方法?

请注意,Bar 有一大堆有用的方法,这种 oop 方法真的很干净。我想避免将我的Bar 对象保留为data.frame 的行(例如bars = data.frame(a = .., b = .., c = ..)),即使它会使BarContainersubset 方法的实现更容易。

【问题讨论】:

    标签: r expression subset r6


    【解决方案1】:

    如果您希望BarContainer$subset 以这种方式工作,您将需要捕获表达式并为BarContainer 中的每个Bar 评估它以获得逻辑向量,然后使用它来子集bars 字段在你的容器中。然后可以将生成的Bar 对象列表传递给BarContainer$new 调用,以返回带有子集Bar 对象的新BarContainer

    您可以使用match.call() 捕获表达式并使用lapply 在每个Bar 的上下文中对其进行评估,但您需要将每个Bar 表示为lapply 中的一个列表才能使其成为一个有效的评估上下文。这意味着实现看起来像这样:

    BarContainer = R6::R6Class(
      ### Class name
      "BarContainer",
      
      ### Public members
      public = list(
        ### Attributes
        bars = c(),
        
        ### Initialize
        initialize = function(input)
        {
          self$bars = input
        },
        
        ### Subset
        subset = function(expr)
        {
          ex <- as.list(match.call()[-1])$exp
          ss <- sapply(self$bars, function(x){
            eval(ex, envir = as.list(x), enclos = parent.frame())})
          return(BarContainer$new(self$bars[ss]))
        }
      )
    )
    

    所以我们可以像这样创建一个示例设置:

    # Create 4 Bars for our container:
    bar1 <- Bar$new(1, 1, 1)
    bar2 <- Bar$new(2, 2, 2)
    bar3 <- Bar$new(3, 3, 3)
    bar4 <- Bar$new(4, 4, 4)
    
    # Populate our container:
    bar_container <- BarContainer$new(c(bar1, bar2, bar3, bar4))
    

    我们的子集函数按预期工作:

    subsetted_container <- bar_container$subset(a > 1 & c < 4)
    
    subsetted_container$bars
    #> [[1]]
    #> <Bar>
    #>   Public:
    #>     a: 2
    #>     b: 2
    #>     c: 2
    #>     clone: function (deep = FALSE) 
    #>     initialize: function (A, B, C) 
    #> 
    #> [[2]]
    #> <Bar>
    #>   Public:
    #>     a: 3
    #>     b: 3
    #>     c: 3
    #>     clone: function (deep = FALSE) 
    #>     initialize: function (A, B, C) 
    

    请记住,子集BarContainer 不会保存原始Bar 对象的副本,而是指向对象本身的指针。因此,如果您更改子集容器中的第一个Bar,它将更改非子集容器中的bar2 和第二个Bar。我猜这无论如何都是期望的行为,但如果不是,您将不得不深度克隆每个 Bar 作为子集方法的一部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-12
      • 2011-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-14
      相关资源
      最近更新 更多