Функции-конструкторы
Часть раздела Объектно-ориентированное программирование путешествия по R на Coddy. Урок 3 из 57.
Поскольку R никогда не проверяет поля объекта S3, каждый объект класса должен создаваться одной функцией — его конструктором. По соглашению низкоуровневый конструктор называется new_ плюс имя класса. Он принимает каждое поле в качестве аргумента, проверяет типы с помощью stopifnot() и возвращает объект:
new_money <- function(amount = double(), currency = character()) {
stopifnot(is.double(amount), is.character(currency))
structure(list(amount = amount, currency = currency), class = "money")
}
m <- new_money(9.5, "EUR")
cat(m$amount, m$currency)Результат:
9.5 EURstopifnot() останавливает программу, когда одно из его условий не равно TRUE, а сообщение указывает на непрошедшую проверку. Неверный тип обнаруживается в момент создания объекта, а не позже, когда его использует какая-либо другая функция:
new_money <- function(amount = double(), currency = character()) {
stopifnot(is.double(amount), is.character(currency))
structure(list(amount = amount, currency = currency), class = "money")
}
m <- new_money("9.5", "EUR")Вывод:
Error in new_money("9.5", "EUR") : is.double(amount) is not TRUE
Calls: new_money -> stopifnot
Execution haltedКонструктор — единственное место, где для класса вызывается structure(), поэтому у каждого объекта одинаковые поля в одинаковом порядке. Код, которому требуется много объектов, вызывает её в цикле или с помощью lapply():
new_point <- function(x = 0, y = 0) {
stopifnot(is.numeric(x), is.numeric(y))
structure(list(x = x, y = y), class = "point")
}
pts <- lapply(1:3, function(i) new_point(i, i * i))
for (p in pts) cat(p$x, p$y, "\n")Результат:
1 1
2 4
3 9 Объект S3 не обязательно должен быть списком. Конструктор может пометить обычный вектор и сохранить дополнительные сведения в качестве атрибутов — именно так работают собственные факторы и даты в R. attr(x, "unit") считывает атрибут:
new_distance <- function(value = double(), unit = "m") {
stopifnot(is.double(value), is.character(unit))
structure(value, unit = unit, class = "distance")
}
d <- new_distance(c(1.5, 2), "km")
print(attr(d, "unit"))
print(unclass(d)[2] * 1000)Результат:
[1] "km"
[1] 2000Задание
ЛегкоЗавершите конструктор new_book(title, author, pages) в Book.R. Он с помощью stopifnot() проверяет, что title и author имеют тип character, а pages — числовой тип, и возвращает список с этими тремя полями и классом book. Затем завершите book_label(b), которая возвращает Dune by Frank Herbert, 412 pages.
Предоставленный код считывает строки title;author;pages. Он передаёт pages как число, если оно состоит из цифр, а в противном случае — как исходный текст, выводит book: и метку для каждого объекта, а когда конструктор останавливает выполнение, выводит rejected: и название.
Ваш код должен находиться в Book.R. В main.R содержится предоставленный код ввода/вывода, и его нельзя редактировать.
Попробуйте сами
source("Book.R")
# Предоставленный код ввода/вывода: оставьте его как есть
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
p <- strsplit(line, ";")[[1]]
pages <- if (grepl("^[0-9]+$", p[3])) as.numeric(p[3]) else p[3]
b <- tryCatch(new_book(p[1], p[2], pages), error = function(e) NULL)
if (is.null(b)) {
cat("rejected: ", p[1], "\n", sep = "")
} else {
cat(class(b), ": ", book_label(b), "\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()Повторение — расчёт зарплаты сотрудниковПотренируйтесь самостоятельно: Онлайн-компилятор R