Menu
Coddy logo textTech

Funções Construtoras

Parte da seção Programação Orientada a Objetos do Journey de R da Coddy. Lição 3 de 57.

Como R nunca verifica os campos de um objeto S3, todo objeto de uma classe deve ser criado por uma única função, seu construtor. Por convenção, o construtor de baixo nível é chamado de new_ mais o nome da classe. Ele recebe cada campo como argumento, verifica os tipos com stopifnot() e retorna o objeto:

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)

Saída:

9.5 EUR

stopifnot() interrompe o programa quando uma de suas condições não é TRUE, e a mensagem nomeia a verificação que falhou. Um tipo incorreto é detectado no momento em que o objeto é construído, e não posteriormente, quando alguma outra função o utiliza:

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")

Saída:

Error in new_money("9.5", "EUR") : is.double(amount) is not TRUE
Calls: new_money -> stopifnot
Execution halted

O construtor é o único lugar que chama structure() para a classe, portanto todo objeto tem os mesmos campos na mesma ordem. O código que precisa de muitos objetos o chama em um loop ou com 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")

Saída:

1 1 
2 4 
3 9 

Um objeto S3 não precisa ser uma lista. Um construtor pode rotular um vetor simples e armazenar informações adicionais como atributos, que é assim que os próprios fatores e datas do R funcionam. attr(x, "unit") lê um atributo:

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)

Saída:

[1] "km"
[1] 2000
challenge icon

Desafio

Fácil

Complete o construtor new_book(title, author, pages) em Book.R. Ele verifica com stopifnot() se title e author são do tipo caractere e se pages é numérico, e retorna uma lista com esses três campos e a classe book. Em seguida, complete book_label(b), que retorna Dune by Frank Herbert, 412 pages.

O código fornecido lê linhas no formato title;author;pages. Ele passa pages como um número quando é composto por dígitos e como o texto original caso contrário, imprime book: e o rótulo de cada objeto, e imprime rejected: e o título quando o construtor interrompe a execução.

Seu código deve estar em Book.R. main.R contém o código fornecido de entrada/saída e não pode ser editado.

Experimente você mesmo

source("Book.R")

# Código de entrada/saída fornecido: mantenha-o como está
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 = "")
  }
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de R online