5

我正在尝试将 S3“数学”组泛型用于自定义类。但是我得到了一个奇怪的结果:log()同时工作log2log10产生错误。下面是一个最小的例子:

# simple class with just the new name
lameclass <- function(x) {
  class(x) <- append(class(x), "lame")
  x
}

# It prints something when Math generics methods are used
Math.lame <- function(x, ...) {
  print("I am lame")
  NextMethod()
}

# an object of the class
lamevector <- lameclass(1:10)

> class(lamevector)
[1] "integer" "lame"

现在尝试调用log

log(lamevector)
[1] "I am lame"
[1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101 2.0794415 2.1972246 2.3025851

以 2 为基数:

log(lamevector, 2)
[1] "I am lame"
[1] 0.000000 1.000000 1.584963 2.000000 2.321928 2.584963 2.807355 3.000000 3.169925 3.321928

以上所有工作。但现在log2包装:

log2(lamevector)
[1] "I am lame"
[1] "I am lame"
Error in log2.default(1:10, 2) :
  2 arguments passed to 'log2' which requires 1

也许有人可以帮助我弄清楚这里发生了什么?log2 实际上是否经历了 2 次通用数学定义并失败了?

4

2 回答 2

4

似乎正在发生的事情NextMethod是没有剥离lame类,所以当log2调用时log,它重新调度到lame方法,现在不再起作用,因为它调用log2时没有base = 2L参数。log2

强制调度正常工作不需要太多工作——只需剥离并重新添加类。(旁白:子类应该被添加,而不是被添加。)

lameclass <- function(x) {
    class(x) <- c("lame", class(x))    # prepend new class
    x
}

Math.lame <- function(x, ...) {
    print("I am lame")
    class(x) <- class(x)[class(x) != "lame"]    # strip lame class
    lameclass(NextMethod())    # re-add lame class to result
}

lamevector <- lameclass(1:5)

log(lamevector)
#> [1] "I am lame"
#> [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379
#> attr(,"class")
#> [1] "lame"    "numeric"
log(lamevector, 2)
#> [1] "I am lame"
#> [1] 0.000000 1.000000 1.584963 2.000000 2.321928
#> attr(,"class")
#> [1] "lame"    "numeric"
log2(lamevector)
#> [1] "I am lame"
#> [1] 0.000000 1.000000 1.584963 2.000000 2.321928
#> attr(,"class")
#> [1] "lame"    "numeric"

我不确定它为什么会这样发送。组泛型有点奇怪,并且调度oldClass而不是class,这可能是问题的一部分,也可能不是问题的一部分。这可能只是一个错误。剥离和重新添加类的习惯用法在其他Math方法中使用,可能是因为这个原因:

MASS:::Math.fractions
#> function (x, ...) 
#> {
#>     x <- unclass(x)
#>     fractions(NextMethod())
#> }
#> <bytecode: 0x7ff8782a1558>
#> <environment: namespace:MASS>
于 2018-08-30T17:13:52.767 回答
2

如评论中所述log2log10不在S3 Math通用范围内。事实上,expexpm1log、和是S4log10通用的,并且是数学组通用的成员。log2log1p

实现您想要做的事情的一种方法是将您的类定义为S4类。

setClass("lame4", slots = c(x = "numeric"))

并定义方法Math组 generic :

setMethod("Math","lame4",function(x) {
                x@x <- callGeneric(x@x)
                x
          }) 
## pretty print 
setMethod("show", "lame4",function(object)print(object@x))

现在让我们测试一下:

l1 <- new("lame4",x=1:10)

然后:

log2(l1)
 [1] 0.000000 1.000000 1.584963 2.000000 2.321928 2.584963 2.807355 3.000000 3.169925 3.321928
> log10(l1)
 [1] 0.0000000 0.3010300 0.4771213 0.6020600 0.6989700 0.7781513 0.8450980 0.9030900 0.9542425
[10] 1.0000000 

这当然不是对您问题的直接回答,而是解释了为什么您的实施不起作用。在这里,我认为使用S4范式是一个好主意,因为您将拥有更强的打字能力,这对数学非常有帮助。S4 方法也适用于 RC/Rcpp 接口。但是,如果您是新手,则有一定的学习曲线(取决于您的开发背景)

于 2018-08-30T09:43:35.947 回答