使用表达式根据属性对 R6Class 进行子集化

Use an expression to subset R6Class based on attributes

我有一个 R6 class BarContainer,其中包含 Bar 的向量,其中 Bar 是另一个 R6 class。我想在 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 = ..)),即使它会在 [=12= 中实现 subset 方法] 容易多了。

如果您希望 BarContainer$subset 以这种方式工作,您将需要捕获表达式并对 BarContainer 中的每个 Bar 求值以获得逻辑向量,然后使用这对容器中的 bars 字段进行子集化。 Bar 对象的结果列表然后可以传递给 BarContainer$new 调用 return 一个新的 BarContainer 与子集 Bar 对象。

您可以使用 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 作为你的子集方法的一部分。