Menu
Coddy logo textTech

Konstruktorfunktionen

Teil des Abschnitts Objektorientierte Programmierung der R-Journey von Coddy. Lektion 3 von 57.

Da R die Felder eines S3-Objekts niemals überprüft, sollte jedes Objekt einer Klasse von einer einzigen Funktion erstellt werden, ihrem Konstruktor. Konventionell wird der Konstruktor auf niedriger Ebene new_ plus dem Klassennamen genannt. Er nimmt jedes Feld als Argument entgegen, überprüft die Typen mit stopifnot() und gibt das Objekt zurück:

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)

Ausgabe:

9.5 EUR

stopifnot() beendet das Programm, wenn eine seiner Bedingungen nicht TRUE ist, und die Meldung benennt die fehlgeschlagene Prüfung. Ein falscher Typ wird in dem Moment erkannt, in dem das Objekt erstellt wird, und nicht erst später, wenn eine andere Funktion es verwendet:

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

Ausgabe:

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

Der Konstruktor ist der einzige Ort, der structure() für die Klasse aufruft, sodass jedes Objekt dieselben Felder in derselben Reihenfolge hat. Code, der viele Objekte benötigt, ruft ihn in einer Schleife oder mit lapply() auf:

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

Ausgabe:

1 1 
2 4 
3 9 

Ein S3-Objekt muss keine Liste sein. Ein Konstruktor kann einen einfachen Vektor kennzeichnen und zusätzliche Informationen als Attribute speichern – so funktionieren auch R-eigene Faktoren und Datumsangaben. attr(x, "unit") liest ein 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)

Ausgabe:

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

Aufgabe

Einfach

Vervollständige den Konstruktor new_book(title, author, pages) in Book.R. Er prüft mit stopifnot(), dass title und author Zeichenketten und pages numerisch ist, und gibt eine Liste mit diesen drei Feldern und der Klasse book zurück. Vervollständige anschließend book_label(b), das Dune by Frank Herbert, 412 pages zurückgibt.

Der bereitgestellte Code liest Zeilen im Format title;author;pages. Er übergibt pages als Zahl, wenn der Wert nur aus Ziffern besteht, und andernfalls als ursprünglichen Text, gibt book: und die Bezeichnung für jedes Objekt aus und gibt rejected: sowie den Titel aus, wenn der Konstruktor abbricht.

Dein Code gehört in Book.R. main.R enthält den bereitgestellten Eingabe-/Ausgabecode und kann nicht bearbeitet werden.

Probier es selbst

source("Book.R")

# Mitgelieferter Ein-/Ausgabe-Code: belasse ihn unverändert
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 dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-R-Compiler