Диспетчеризация по двум аргументам
Часть раздела Объектно-ориентированное программирование путешествия по R на Coddy. Урок 29 из 57.
Универсальная функция S4 может выбирать свой метод по классам нескольких аргументов. Сигнатура перечисляет по одному классу для каждого аргумента, поэтому у каждой пары классов может быть собственный метод:
setClass("Asteroid", slots = c(size = "numeric"))
setClass("Ship", slots = c(name = "character"))
invisible(setGeneric("collide", function(a, b) standardGeneric("collide")))
setMethod("collide", signature("Asteroid", "Ship"), function(a, b) paste(b@name, "is hit"))
setMethod("collide", signature("Ship", "Ship"), function(a, b) "two ships bump")
rock <- new("Asteroid", size = 3)
enterprise <- new("Ship", name = "Enterprise")
print(collide(rock, enterprise))
print(collide(enterprise, enterprise))Результат:
[1] "Enterprise is hit"
[1] "two ships bump"Класс "ANY" соответствует каждому классу. Метод для signature("ANY", "ANY") является запасным вариантом, который запускается, когда не подходит более специфичная пара:
setClass("Ship", slots = c(name = "character"))
invisible(setGeneric("collide", function(a, b) standardGeneric("collide")))
setMethod("collide", signature("Ship", "Ship"), function(a, b) "two ships bump")
setMethod("collide", signature("ANY", "ANY"), function(a, b) "nothing happens")
s <- new("Ship", name = "A")
cat(collide(s, s), "/", collide(s, 42), "/", collide("x", "y"))Вывод:
two ships bump / nothing happens / nothing happensПорядок аргументов имеет значение: signature("A", "B") и signature("B", "A") — это разные методы. В сигнатуре также могут встречаться базовые классы, такие как numeric и character:
invisible(setGeneric("join", function(x, y) standardGeneric("join")))
setMethod("join", signature("character", "numeric"), function(x, y) paste(x, "number", y))
setMethod("join", signature("numeric", "character"), function(x, y) paste(y, "after number", x))
print(join("item", 3))
print(join(3, "item"))Результат:
[1] "item number 3"
[1] "item after number 3"Класс "missing" в сигнатуре соответствует аргументу, который вообще не был передан, поэтому один обобщённый метод может предоставлять краткую и полную формы:
invisible(setGeneric("greet", function(name, greeting) standardGeneric("greet")))
setMethod("greet", signature("character", "missing"), function(name, greeting) paste("Hello,", name))
setMethod("greet", signature("character", "character"), function(name, greeting) paste0(greeting, ", ", name))
print(greet("Ada"))
print(greet("Ada", "Good morning"))Вывод:
[1] "Hello, Ada"
[1] "Good morning, Ada"Задание
ЛегкоЦены на доставку зависят от предмета и зоны. Классы объявляются в Item.R (Letter, Box, каждый со слотом kg) и Zone.R (Domestic, Abroad, каждый со слотом name). В Rates.R создайте обобщённую функцию shipping(item, zone) и по одному методу для каждой пары:
Letterдома: 1; за границей: 3.Boxдома: 5 плюс 2 за каждый кг; за границей: 12 плюс 4 за каждый кг.- Для любой другой пары остановите выполнение с сообщением
no rate for this item and zone(используйтеcall. = FALSE).
Предоставленный код считывает строки box home 2.5 (предмет, зона, кг), при этом неизвестные слова остаются обычным текстом, и выводит box to home: 10.00 или error: и сообщение.
Ваш код должен находиться в Item.R, Zone.R и Rates.R. В main.R находится предоставленный код ввода/вывода, и его нельзя редактировать.
Попробуйте сами
source("Item.R")
source("Zone.R")
source("Rates.R")
# Предоставленный код ввода/вывода: оставьте его как есть
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
p <- strsplit(line, " ")[[1]]
kg <- as.numeric(p[3])
item <- switch(p[1], letter = new("Letter", kg = kg), box = new("Box", kg = kg), kg)
zone <- switch(p[2], home = new("Domestic", name = "home"), abroad = new("Abroad", name = "abroad"), p[2])
out <- tryCatch(sprintf("%s to %s: %.2f", p[1], p[2], shipping(item, zone)),
error = function(e) paste("error:", conditionMessage(e)))
cat(out, "\n", sep = "")
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы S3
Работа с файламиСписки с классомФункции-конструкторыМетоды print и formatВалидаторы и вспомогательные функцииПовторение — температуры4Инкапсуляция в R
Замыкания как объектыОкружения как объектыФункции доступаЗащита состоянияПовторение — парковочный счётчик7S4 Наследование
Расширение с помощью containscallNextMethodВиртуальные классыПроверка типов с помощью is()Повторение — смотритель зоопарка10Шаблоны проектирования, часть 2
Шаблон «Декоратор»Шаблон «Строитель»Шаблон «Состояние»Шаблон «Итератор»2Обобщённые функции и методы S3
Обобщённые функции и UseMethodМетоды по умолчаниюМетоды для базовых обобщённых функцийОсновы NextMethodПовторение — площади фигур5Классы S4
Определение классов S4Слоты и доступПроверка корректностиМетоды showПовторение — каталог товаров8Ссылочные классы
Определение ссылочных классовМетоды и обновление полейСемантика копированияНаследование и callSuperПовторение — очередь задач11Проект: Управление библиотекой
Книги и читателиВыдача книг3Наследование S3
Векторы классовПорядок поиска методовРасширение с помощью NextMethodПроверка с помощью inherits()Повторение — расчёт зарплаты сотрудников6S4: обобщения и методы
Создание обобщенийМетоды для встроенных объектовДиспетчеризация по двум аргументамsetValidity и validObjectПовторение — деньги и валютаПотренируйтесь самостоятельно: Онлайн-компилятор R