Использование NextMethod с внутренними дженериками

С универсальным шаблоном, использующим 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 с внутренними дженериками, и если да, то какие аргументы и т. д. необходимы?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
0
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Похоже, что для 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 является внутренним универсальным (в .internalGenerics) и ведет себя не так, как cbind.
Mikael Jagan 13.08.2024 17:30
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"))
Mikael Jagan 13.08.2024 17:32

Глядя на methods(as.vector), у него есть as.vector,ANY-method метод S4. Так что, возможно, в этом и есть разница.

MrFlick 13.08.2024 17:34

Таким образом, cbind может отправлять метод, специфичный для класса cbind.foo, но не устанавливает .Generic, .Method, .Class, как сказал Микаэль. Таким образом, cbind.foo должен быть реализован как обычная функция и не может выполнять наследование. Но наследование можно как бы смоделировать, изменив класс x и вызвав cbind(x) внутри cbind.foo.

user615536 13.08.2024 17:36

Разница в том, что do_asvector в coerce.c использует DispatchOrEval, а do_bind в bind.c использует applyClosure напрямую, т. е. не строит правильный фрейм оценки для вызова метода.

Mikael Jagan 13.08.2024 17:38

Следовательно, мне кажется, что это ошибка на уровне C do_bind.

Mikael Jagan 13.08.2024 17:39

Adv-R 13.7.2: «внутренние дженерики... не вызывайте UseMethod(), а вместо этого вызывайте функции C DispatchGroup() или DispatchOrEval()». Если это точно, то вызов applyClosure вместо DispatchOrEval вполне может быть ошибкой.

user615536 13.08.2024 17:43

С другой стороны, поскольку cbind и rbind отправляются на ..., а не на x, понятно, что они действуют по-разному. Фактически help("cbind") четко документирует, что отправка в этих случаях действительно является исключительной. Я все равно напишу отчет об ошибке, главным образом потому, что c, который также отправляется на ..., не имеет этой проблемы.

Mikael Jagan 13.08.2024 17:51
Ответ принят как подходящий

Такое поведение обусловлено специальным механизмом отправки на ... на уровне 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

Другие вопросы по теме