Menu
Coddy logo textTech

Fonctions constructrices

Fait partie de la section Programmation orientée objet du Journey R de Coddy. Leçon 3 sur 57.

Comme R ne vérifie jamais les champs d’un objet S3, chaque objet d’une classe doit être créé par une seule fonction, son constructeur. Par convention, le constructeur de bas niveau est appelé new_ suivi du nom de la classe. Il prend chaque champ comme argument, vérifie les types avec stopifnot() et renvoie l’objet :

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)

Sortie :

9.5 EUR

stopifnot() arrête le programme lorsque l’une de ses conditions n’est pas TRUE, et le message indique la vérification qui a échoué. Un type incorrect est détecté au moment où l’objet est créé, et non plus tard lorsqu’une autre fonction l’utilise :

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

Sortie :

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

Le constructeur est le seul endroit qui appelle structure() pour la classe, de sorte que chaque objet possède les mêmes champs dans le même ordre. Le code qui a besoin de nombreux objets l’appelle dans une boucle ou avec 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")

Sortie :

1 1 
2 4 
3 9 

Un objet S3 ne doit pas nécessairement être une liste. Un constructeur peut étiqueter un vecteur simple et stocker des informations supplémentaires sous forme d’attributs, ce qui explique le fonctionnement des facteurs et des dates de R. attr(x, "unit") lit un attribut :

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)

Sortie :

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

Défi

Facile

Complétez le constructeur new_book(title, author, pages) dans Book.R. Il vérifie avec stopifnot() que title et author sont de type caractère et que pages est numérique, puis renvoie une liste contenant ces trois champs et la classe book. Complétez ensuite book_label(b), qui renvoie Dune by Frank Herbert, 412 pages.

Le code fourni lit des lignes title;author;pages. Il transmet pages sous forme de nombre lorsqu'il est composé de chiffres, et sous forme de texte original dans le cas contraire, affiche book: ainsi que le libellé de chaque objet, puis affiche rejected: et le titre lorsque le constructeur s'arrête.

Votre code doit être placé dans Book.R. main.R contient le code d'entrée/sortie fourni et ne peut pas être modifié.

Essayez vous-même

source("Book.R")

# Code d'entrée/sortie fourni : le garder tel quel
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 iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur R en ligne