#r #methods #signature #s4
#r #методы #подпись #s4
Вопрос:
Я пытаюсь реализовать разные версии метода, которые имеют похожие, но разные требования к вводу. Исходя из языков со статической и строгой типизацией, я пытаюсь написать эти методы таким образом, чтобы свести к минимуму возможность для целевых пользователей (это для пакета) использовать методы непреднамеренными способами. Разные версии метода имеют разное количество параметров, и я обнаружил, что поддержка версий метода с двумя или более параметрами позволяет вводить бессмыслицу в версию метода, которая ожидает только один параметр. Вот тривиальный пример:
setGeneric(
"foo",
function(a, b) {
standardGeneric("foo")
})
setMethod(
"foo",
signature(a = "numeric"),
function(a) {
abs(a)
})
setMethod(
"foo",
signature(a = "numeric", b = "numeric"),
function(a, b) {
abs(c(a, b))
})
Он работает, как и ожидалось, со следующими входными данными (некоторые допустимы, некоторые нет, и выдают ошибки, как и должны):
> foo(-1)
[1] 1
> foo(-1, -2)
[1] 1 2
> foo("cat")
Error in (function (classes, fdef, mtable) :
unable to find an inherited method for function ‘foo’ for signature ‘"character", "missing"’
> foo()
Error in (function (classes, fdef, mtable) :
unable to find an inherited method for function ‘foo’ for signature ‘"missing", "missing"’
> foo(-1, -2, "cat")
Error in foo(-1, -2, "cat") : unused argument ("cat")
Но тогда есть один сценарий, в котором он ведет себя не так, как должно быть приемлемо:
> foo(-1, "cat")
[1] 1
Это вызывает первую сигнатуру метода и игнорирует второй параметр. Это потенциально серьезная логическая ошибка для пользователей, что является проблемой для меня, потому что мои целевые пользователи не являются компьютерными специалистами; большинство из них не понимают, что такое логические ошибки, насколько они опасны или как их отслеживать. Есть ли в R способ настроить методы так, чтобы этот последний пример foo(-1, "cat")
выдавал ошибку, а не создавал у пользователя впечатление, что все хорошо?
Обратите внимание, что, хотя разные версии методов, над которыми я работаю, фундаментально связаны, фактические реализации для каждого из них сильно отличаются. Я мог бы использовать функции с необязательными аргументами, но это потребовало бы нескольких проверок для запуска совершенно разных больших фрагментов кода. Я надеялся избежать этого, потому что это не особенно чисто или элегантно.
Ответ №1:
Вы можете заставить его работать, если используете специальную "missing"
подпись следующим образом:
setGeneric(
"foo",
function(a, b) {
standardGeneric("foo")
})
setMethod(
"foo",
signature(a = "numeric", b = "missing"),
function(a, b) {
abs(a)
})
setMethod(
"foo",
signature(a = "numeric", b = "numeric"),
function(a, b) {
abs(c(a, b))
}
)
Проверка вызовов:
foo(-1)
#[1] 1
foo(-1, -2)
#[1] 1 2
foo(-1, "cat")
#Error in (function (classes, fdef, mtable) :
# unable to find an inherited method for function ‘foo’ for
# signature ‘"numeric", "character"’
foo("cat")
# Error in (function (classes, fdef, mtable) :
# unable to find an inherited method for function ‘foo’ for
# signature ‘"character", "missing"’
Аналогично, foo()
и foo(-1, -2, "cat")
, как и раньше, завершается с ошибкой.
Как вы сами заметили, если вы добавите некоторые print
инструкции в свои методы, вы увидите, что вызов foo(-1, "cat")
отправляется вашему методу с одним аргументом. Причина, по которой ошибка не выдается, заключается в том, что обещание для b
аргумента никогда не требуется и не оценивается. Я не очень хорошо разбираюсь в деталях правил диспетчеризации метода S4 в подобных случаях и в том, ожидалось ли это. Но в любом случае, в свете «отсутствующей» сигнатуры, я полагаю, что хорошей практикой является то, что аргументы методов всегда совпадают с аргументами общих методов; и я почти уверен, что что-то вроде R CMD check
будет жаловаться, если они не совпадают.
Комментарии:
1. Я полагаю, что такое поведение было задумано разработчиками. Проверка R CMD не будет жаловаться на разные сигнатуры между методами, если все они являются общими аргументами; возможно, вы захотите использовать общий
numeric
метод и использовать его только для(numeric,numeric)
примера (я полагаю, вы могли бы использоватьANY
для этой цели)