Menu
Coddy logo textTech

Методы и обновление полей

Часть раздела Объектно-ориентированное программирование путешествия по R на Coddy. Урок 38 из 57.

Методы помещаются в список methods функции setRefClass(). Внутри метода поля являются обычными именами, а <<- изменяет поле. В среде выполнения Coddy R выводит сообщение о необязательном пакете codetools, когда определяется класс с методами, поэтому заключите определение в suppressMessages():

Counter <- suppressMessages(setRefClass("Counter",
  fields = list(count = "numeric"),
  methods = list(
    add = function(n = 1) {
      count <<- count + n
    },
    report = function() paste("count is", count)
  )
))
c1 <- Counter$new(count = 0)
c1$add()
c1$add(5)
print(c1$report())

Вывод:

[1] "count is 6"

.self — это объект, для которого был вызван метод. Метод, завершающийся вызовом invisible(.self), возвращает объект, поэтому вызовы можно объединять в цепочку:

Counter <- suppressMessages(setRefClass("Counter",
  fields = list(count = "numeric"),
  methods = list(
    add = function(n = 1) {
      count <<- count + n
      invisible(.self)
    }
  )
))
c1 <- Counter$new(count = 0)
c1$add(2)$add(3)$add(4)
print(c1$count)

Результат:

[1] 9

Метод initialize выполняется внутри $new(). initFields(...) задаёт переданные поля; после этого метод может задать значения по умолчанию для остальных. Методы вызывают друг друга по имени:

Account <- suppressMessages(setRefClass("Account",
  fields = list(owner = "character", balance = "numeric"),
  methods = list(
    initialize = function(...) {
      initFields(...)
      if (length(balance) == 0) balance <<- 0
      invisible(.self)
    },
    deposit = function(x) balance <<- balance + x,
    deposit_twice = function(x) {
      deposit(x)
      deposit(x)
    }
  )
))
a <- Account$new(owner = "Ada")
a$deposit_twice(10)
print(a$balance)

Результат:

[1] 20

Внутри метода обычное <- для имени поля создаёт локальную переменную с этим именем и оставляет поле без изменений. Используйте <<- при каждом изменении поля:

Counter <- suppressMessages(setRefClass("Counter",
  fields = list(count = "numeric"),
  methods = list(
    broken = function() count <- count + 1,
    working = function() count <<- count + 1
  )
))
c1 <- Counter$new(count = 0)
c1$broken()
c1$broken()
c1$working()
print(c1$count)

Результат:

[1] 1
challenge icon

Задание

Легко

Завершите реализацию ссылочного класса Stack в Stack.R. Он хранит свои элементы в поле items (тип character, последний элемент находится сверху) и имеет четыре метода:

  • push(x) добавляет x наверх и возвращает invisible(.self), поэтому вызовы push можно объединять в цепочку;
  • pop() удаляет и возвращает верхний элемент или возвращает nothing, если стек пуст;
  • peek() возвращает верхний элемент, не удаляя его, или nothing;
  • size() возвращает количество элементов.

Определение уже обёрнуто в suppressMessages(). Предоставленный код считывает команды push a b, pop и peek, затем добавляет ещё два элемента одним цепочечным вызовом и выводит размер.

Ваш код должен находиться в Stack.R. В main.R находится предоставленный код ввода/вывода, и его нельзя редактировать.

Попробуйте сами

source("Stack.R")

# Предоставленный код ввода/вывода: оставьте его как есть
input <- suppressWarnings(readLines(file("stdin")))
s <- Stack$new()
for (cmd in input) {
  if (startsWith(cmd, "push ")) {
    items <- strsplit(substring(cmd, 6), " ")[[1]]
    for (it in items) s$push(it)
  } else if (cmd == "pop") {
    cat("popped ", s$pop(), "\n", sep = "")
  } else if (cmd == "peek") {
    cat("top ", s$peek(), "\n", sep = "")
  }
}
s$push("x")$push("y")
cat("size after two chained pushes: ", s$size(), "\n", sep = "")
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор R