【问题标题】:emulating multiple dispatch using S3 for "+" method - possible?使用 S3 为“+”方法模拟多个调度 - 可能吗?
【发布时间】:2012-12-01 12:27:06
【问题描述】:

我有两个类(ab),我想为它们定义 + 方法。 对于这两个类的四种可能组合,我需要不同的方法,即:

a + a  method 1
a + b  method 2
b + a  method 3
b + b  method 4

我知道我可以使用 S4 进行多次调度,但我想知道是否有办法使用 S3 来模拟这种行为。我的方法如下:

a <- "b"
class(a) <- "a"

b <- "e"
class(b) <- "b"

Ops.a <- function(e1, e2){
  if (class(e1) == "a" &
      class(e2) == "a")
    print("a & a")
  if (class(e1) == "a" &
        class(e2) == "b")
    print("a & b")
  if (class(e1) == "b" &
        class(e2) == "a")
    print("b & a")
  NULL
}

a + a
a + b
b + a

所有这些都可以正常工作,但是当然没有定义以下内容。

b + b

现在为了讨论这个案例,我添加了另一个方法定义。

Ops.b <- function(e1, e2){
  if (class(e1) == "b" &
        class(e2) == "b")
    print("b & b")
  NULL
}

这将导致b + b 工作,但现在a + bb + a 方法不一致,会导致错误。

> a + b
error in a + b : non-numeric argument for binary operator
additional: warning:
incompatible methods ("Ops.a", "Ops.b") for "+"

有没有办法使用 S3 正确定义所有四种情况?

【问题讨论】:

    标签: r r-s3


    【解决方案1】:

    您可以通过将+.a+.b 定义为相同的函数来实现。例如:

    a <- "a"
    class(a) <- "a"
    b <- "b"
    class(b) <- "b"
    
    `+.a` <- function(e1, e2){
      paste(class(e1), "+", class(e2))
    }
    `+.b` <- `+.a`
    
    a+a
    # [1] "a + a"
    a+b
    # [1] "a + b"
    b+a
    # [1] "b + a"
    b+b
    # [1] "b + b"
    
    # Other operators won't work
    a-a
    # Error in a - a : non-numeric argument to binary operator
    

    如果你定义了Ops.aOps.b,它也会定义其他操作符的操作,可以通过函数中的.Generic来访问:

    ##### Start a new R session so that previous stuff doesn't interfere ####
    a <- "a"
    class(a) <- "a"
    b <- "b"
    class(b) <- "b"
    
    Ops.a <- function(e1, e2){
      paste(class(e1), .Generic, class(e2))
    }
    
    Ops.b <- Ops.a
    
    a+a
    # [1] "a + a"
    a+b
    # [1] "a + b"
    b+a
    # [1] "b + a"
    b+b
    # [1] "b + b"
    
    
    # Ops covers other operators besides +
    a-a
    # [1] "a - a"
    a*b
    # [1] "a * b"
    b/b
    # [1] "b / b"
    

    更新:我在玩这个时发现的另一件事。如果你把它放在一个包中,你会得到“非数字参数”错误和“不兼容的运算符”警告。这是因为 R 仅适用于多个运算符,前提是它们是完全相同的对象,在内存中具有相同的地址 - 但不知何故,在构建和加载包时,这两个函数失去了这个确切的身份。 (您可以使用pryr::address() 进行检查)

    我发现有效的一件事是在加载包时显式注册 S3 方法。例如,这将进入您的包中:

    # Shows the classes of the two objects that are passed in
    showclasses <- function(e1, e2) {
      paste(class(e1), "+", class(e2))
    }    
    
    .onLoad <- function(libname, pkgname) {
      registerS3method("+", "a", showclasses)
      registerS3method("+", "b", showclasses)
    }
    

    在这种情况下,这两个方法指向内存中完全相同的对象,并且可以正常工作(尽管有点小技巧)。

    【讨论】:

    • 我喜欢 R 人回答问题。
    • “有点小技巧”,但是很棒。谢谢!
    【解决方案2】:

    你不能使用那个策略。正如您在帮助(Ops)页面中发现和记录的那样,这是特别禁止的。

    "如果只为一个参数找到一个方法或为两个参数找到相同的方法,则使用该方法。如果找到不同的方法,则会出现有关“不兼容方法”的警告:在这种情况下或如果没有方法发现任一参数都使用了内部方法。”

    因此,您需要将所有案例放入同一个方法中。 (经过测试,确实成功了。)

    【讨论】:

      【解决方案3】:

      如果只调用具有相反参数的运算符呢?

      Ops.b <- function(e1, e2){
        if (class(e1) == "b" &
              class(e2) == "b")
          print("b & b")
        if (class(e1) =="b" & class(e2)=="a")
          e2+e1
        NULL
      }
      

      但我强烈建议使用适当的多重调度,因此为此使用 S4。看 Combining S4 and S3 methods in a single functionAdding S4 dispatch to base R S3 generic.

      【讨论】:

      猜你喜欢
      • 2017-07-11
      • 1970-01-01
      • 1970-01-01
      • 2010-10-11
      • 2013-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-12
      相关资源
      最近更新 更多