С универсальным шаблоном, использующим UseMethod, NextMethod работает так, как я ожидаю:
f <- function(...) UseMethod("f")
f.default <- function(...) 5
f.foo <- function(...) {print("hi"); NextMethod()}
x <- structure(1, class = "foo")
f(x) # prints "hi", returns 5
Со следующим внутренним дженериком аналогичный подход не работает:
# NB: base::cbind is an internal generic
cbind.default <- function(...) 5
cbind.foo <- function(...) {print("hi"); NextMethod()}
cbind(x) # prints "hi", Error in NextMethod() : generic function not specified
Можно ли использовать NextMethod с внутренними дженериками, и если да, то какие аргументы и т. д. необходимы?





Похоже, что для cbind метод «.default» для функции по сути ничего не делает. Он не определен и фактически не вызывается. Так что по сути нет NextMethod, который можно было бы найти. Вы можете изменить класс внутри своего собственного метода, чтобы добиться желаемого поведения. Здесь мы зачистим класс и попробуем еще раз вызвать на базу cbind.
cbind.foo <- function(x, ...) {print("hi"); cbind(unclass(x))}
# [1] "hi"
# [,1]
# [1,] 1
Он использует значение 1 из x, а не значение 5 из cbind.default, потому что оно никогда не вызывается.
as.vector.foo <- function(x, mode = "any") { print("foo"); NextMethod() }; as.vector.default <- function(x, mode = "any") { print("default"); x }; as.vector(structure(0, class = "foo"))Глядя на methods(as.vector), у него есть as.vector,ANY-method метод S4. Так что, возможно, в этом и есть разница.
Таким образом, cbind может отправлять метод, специфичный для класса cbind.foo, но не устанавливает .Generic, .Method, .Class, как сказал Микаэль. Таким образом, cbind.foo должен быть реализован как обычная функция и не может выполнять наследование. Но наследование можно как бы смоделировать, изменив класс x и вызвав cbind(x) внутри cbind.foo.
Разница в том, что do_asvector в coerce.c использует DispatchOrEval, а do_bind в bind.c использует applyClosure напрямую, т. е. не строит правильный фрейм оценки для вызова метода.
Следовательно, мне кажется, что это ошибка на уровне C do_bind.
Adv-R 13.7.2: «внутренние дженерики... не вызывайте UseMethod(), а вместо этого вызывайте функции C DispatchGroup() или DispatchOrEval()». Если это точно, то вызов applyClosure вместо DispatchOrEval вполне может быть ошибкой.
С другой стороны, поскольку cbind и rbind отправляются на ..., а не на x, понятно, что они действуют по-разному. Фактически help("cbind") четко документирует, что отправка в этих случаях действительно является исключительной. Я все равно напишу отчет об ошибке, главным образом потому, что c, который также отправляется на ..., не имеет этой проблемы.
Такое поведение обусловлено специальным механизмом отправки на ... на уровне C do_bind (см. bind.c ). Он не использует традиционный механизм отправки, описанный в help("NextMethod") и реализованный на уровне C Dispatch[Any]OrEval() (см. eval.c). Важно отметить, что поиск метода завершается успешно, но вызов метода не оценивается в среде, определяющей переменные .Generic, .Method, .Class и т. д., необходимые для отправки NextMethod.
С одной стороны, хорошо документировано, что механизм отправки, используемый [cr]bind, является исключительным (см. раздел «Отправка» в help("cbind")). С другой стороны, c концептуально очень параллелен [cr]bind, также выполняет внутреннюю отправку ..., но c не имеет этого ограничения:
> m <- function(...) { cat("a\n"); NextMethod() }
> for (generic in c("c", "cbind", "rbind")) .S3method(generic, "a", m)
> x <- structure(0, class = "a")
> c(x)
a
[1] 0
> cbind(x)
a
Error in NextMethod() : generic function not specified
> rbind(x)
a
Error in NextMethod() : generic function not specified
> debug(m)
> c(x)
debugging in: c.a(x)
debug at #1: {
cat("a\n")
NextMethod()
}
Browse[1]> ls(all.names = TRUE)
[1] "..." ".Class" ".Generic" ".GenericCallEnv"
[5] ".GenericDefEnv" ".Group" ".Method"
Browse[1]> Q
> cbind(x)
debugging in: cbind(deparse.level, ...)
debug at #1: {
cat("a\n")
NextMethod()
}
Browse[1]> ls(all.names = TRUE)
[1] "..."
Browse[1]> Q
> rbind(x)
debugging in: rbind(deparse.level, ...)
debug at #1: {
cat("a\n")
NextMethod()
}
Browse[1]> ls(all.names = TRUE)
[1] "..."
Browse[1]> Q
Мне кажется совершенно «неправильным», что [cr]bind и c здесь должны вести себя по-разному. Итак, я подал отчет об ошибке: https://bugs.r-project.org/show_bug.cgi?id=18779
as.vectorявляется внутренним универсальным (в.internalGenerics) и ведет себя не так, какcbind.