R необязательные аргументы для S4 setMethod

Я заинтересован в установке новых методов для универсальных функций. Например, допустим, у меня есть новый класс (например, coolClass). Я мог бы написать оболочку для вычисления log10 этого класса и легко установить метод с помощью следующего кода:

setMethod("Math", c(x="coolClass"),
          function(x)
          {
            op = .Generic[[1]]
            switch(op,
                   `log10` = log10_for_coolClass(x),
                   stop("Undefined operation")
            )
          }
)

Однако я не могу понять, как настроить метод для передачи нескольких аргументов. Например, общий метод log. Запуск getGeneric("log") показывает следующее:

> getGeneric("log")
standardGeneric for "log" defined from package "base"
  belonging to group(s): Math 

function (x, ...) 
standardGeneric("log", .Primitive("log"))
<bytecode: 0x2f9a958>
<environment: 0x2f937f0>
Methods may be defined for arguments: x
Use  showMethods("log")  for currently available ones.

Увидев это, я ожидаю, что смогу написать следующее, чтобы иметь возможность передать необязательный аргумент base.

setMethod("Math", signature(x="coolClass",...),
          function(x, ...)
          {
            op = .Generic[[1]]
            switch(op,
                   `log` = log_for_coolClass(x, ...),
                   stop("Undefined operation")
            )
          }
)

Но я получаю следующую ошибку:

Error in matchSignature(signature, fdef, where) : 
  '...' used in an incorrect context

Пробовал без ... в signature получаю другую ошибку:

Error in rematchDefinition(definition, fdef, mnames, fnames, signature) : 
   methods can add arguments to the generic ‘Math’ only if '...' is an argument to the generic

Что мне кажется странным, учитывая, что журнал getGeneric показывает ... в методе.

Любые идеи? Есть ли способ зафиксировать дополнительные аргументы? Я пытаюсь освоиться с методами S4, но не понимаю, как передавать необязательные аргументы. Если это невозможно, я был бы признателен, если бы кто-нибудь объяснил, как работает функция log, например, учитывая, что функция является частью группы Math, но принимает несколько аргументов.

Обновить

Любопытно, как отмечено ниже, я могу использовать setMethod непосредственно на log со следующим:

setMethod("log", signature(x="big.matrix"),
          function(x, base=exp(1))
            {
            log_for_coolClass(x, base=base)
          }
          )

Однако это не совсем успокаивает мое любопытство. Например, кажется странным повторяться в коде, если я создаю много новых методов в группе Math. В идеале это выглядело бы примерно так:

setMethod("Math", c(x="coolClass"),
          function(x, base=exp(1))
          {
            op = .Generic[[1]]
            switch(op,
                   `log10` = log10_for_coolClass(x),
                   `log` = log_for_coolClass(x, base=base),
                   stop("Undefined operation")
            )
          }
)

r oop s4
person cdeterman    schedule 21.11.2014    source источник


Ответы (1)


Вот класс

.A <- setClass("A", representation(x="numeric"))

Для функции типа log у нас есть

> getGeneric("log")
standardGeneric for "log" defined from package "base"
  belonging to group(s): Math 

function (x, ...) 
standardGeneric("log", .Primitive("log"))
<bytecode: 0x2b41b28>
<environment: 0x2b39df8>
Methods may be defined for arguments: x
Use  showMethods("log")  for currently available ones.

где подпись включает .... Итак, мы пишем

setMethod("log", "A", function(x, ...) {
    log(x@x, ...)
})

и иметь успех

> log(.A(x=c(1, 2, NA)))
[1] 0.0000000 0.6931472        NA
> log(.A(x=c(1, 2, NA)), base=2)
[1]  0  1 NA

Далее: лог10

> getGeneric("log10")
standardGeneric for "log10" defined from package "base"
  belonging to group(s): Math 

function (x) 
standardGeneric("log10", .Primitive("log10"))
<bytecode: 0x2b4a700>
<environment: 0x2b43138>
Methods may be defined for arguments: x
Use  showMethods("log10")  for currently available ones.

В универсальном методе нет аргументов ..., поэтому мы в основном застряли — напишите свой собственный универсальный и реализуйте методы, которые включают ..., или напишите log10,A-метод, который не имеет аргумента ....

Тот же принцип в отношении ... применяется к групповым обобщениям — групповое обобщение Math не имеет точек.

> getGeneric("Math")
groupGenericFunction for "Math" defined from package "base"

function (x) 
standardGeneric("Math")
<bytecode: 0x2771d40>
<environment: 0x275ee20>
Methods may be defined for arguments: x
Use  showMethods("Math")  for currently available ones.

поэтому наш метод (реализующий все математические операции, а не только протоколирование; обратите внимание, что мы обычно не ссылаемся явно на .Generic) для объектов нашего класса может быть

setMethod("Math", "A", function(x) {
    callGeneric(x@x)
})

и теперь у нас есть методы для всех математических функций, например,

> sqrt(.A(x=1:4))
[1] 1.000000 1.414214 1.732051 2.000000

Кроме того, наш вариант журнала, принимающий ..., все еще доступен, поэтому мы можем как определить поведение нашего класса для всех дженериков Math, так и предоставить специализированные реализации для конкретных дженериков.

Вероятно, в реализации этих групповых дженериков есть ошибка, которую следует устранить. Если в новом сеансе мы определили наш класс и Math generic, но не log функцию с ... аргументом, мы все равно можем вызывать

> log(.A(x=1:4))
[1] 0.0000000 0.6931472 1.0986123 1.3862944
> log(.A(x=1:4), base=2)
[1] 0.0000000 0.6931472 1.0986123 1.3862944

где второй аргумент игнорируется, но на самом деле должен вызвать ошибку.

person Martin Morgan    schedule 21.11.2014
comment
Ваш ответ пришел как раз в тот момент, когда я обнаружил setMethod прямо на log. Интересно получить доступ ко всем математическим операциям с помощью callGeneric, но мой класс требует более сложных функций, которые я пишу отдельно и должен вызывать. Таким образом, похоже, я должен установить определенные методы отдельно. Я надеялся быть более кратким (см. обновленный вопрос), но если это единственный способ. - person cdeterman; 21.11.2014