setValidity und validObject
Teil des Abschnitts Objektorientierte Programmierung der R-Journey von Coddy. Lektion 30 von 57.
setValidity("Class", function(object) ...) fügt die Gültigkeitsregel hinzu, nachdem die Klasse deklariert wurde, wodurch ein langer Aufruf von setClass() kurz bleibt. Wie setGeneric() gibt die Funktion einen Wert zurück, der auf der obersten Ebene ausgegeben wird; deshalb solltest du sie in invisible() einschließen:
setClass("Account", slots = c(owner = "character", balance = "numeric"))
invisible(setValidity("Account", function(object) {
if (object@balance < 0) return("balance must not be negative")
TRUE
}))
msg <- tryCatch(new("Account", owner = "Ada", balance = -1), error = function(e) conditionMessage(e))
cat(msg)Ausgabe:
invalid class “Account” object: balance must not be negativeÄnderungen sollten über Methoden vorgenommen werden, die das Ergebnis prüfen. Eine Methode withdraw() ändert den Slot, ruft validObject() auf und gibt das Objekt nur zurück, wenn es weiterhin gültig ist:
setClass("Account", slots = c(balance = "numeric"))
invisible(setValidity("Account", function(object) {
if (object@balance < 0) "balance must not be negative" else TRUE
}))
invisible(setGeneric("withdraw", function(acc, amount) standardGeneric("withdraw")))
setMethod("withdraw", "Account", function(acc, amount) {
acc@balance <- acc@balance - amount
validObject(acc)
acc
})
a <- new("Account", balance = 50)
a <- withdraw(a, 20)
msg <- tryCatch({ a <- withdraw(a, 100); "done" }, error = function(e) "refused")
cat(msg, a@balance)Ausgabe:
refused 30Ein Zugriffspaar kann aus Generics bestehen: balance(x) liest, und ein ersetzendes Generic "balance<-" schreibt. Seine Methode überprüft das Objekt, daher wird balance(a) <- -5 wie jede andere ungültige Änderung abgelehnt:
setClass("Account", slots = c(balance = "numeric"),
validity = function(object) if (object@balance < 0) "balance must not be negative" else TRUE)
invisible(setGeneric("balance", function(x) standardGeneric("balance")))
invisible(setGeneric("balance<-", function(x, value) standardGeneric("balance<-")))
setMethod("balance", "Account", function(x) x@balance)
setMethod("balance<-", "Account", function(x, value) {
x@balance <- value
validObject(x)
x
})
a <- new("Account", balance = 10)
balance(a) <- 25
msg <- tryCatch({ balance(a) <- -5; "changed" }, error = function(e) "refused")
cat(balance(a), msg)Ausgabe:
25 refusedvalidObject(x, test = TRUE) bricht nicht ab. Für ein gültiges Objekt gibt es TRUE zurück, andernfalls den Problemtext, was für einen Bericht praktisch ist:
setClass("Box", slots = c(w = "numeric"),
validity = function(object) if (object@w <= 0) "w must be positive" else TRUE)
b <- new("Box", w = 2)
print(validObject(b, test = TRUE))
b@w <- 0
print(validObject(b, test = TRUE))Ausgabe:
[1] TRUE
[1] "w must be positive"Aufgabe
EinfachDie Klasse Account in Account.R verfügt über die Slots owner und balance. Vervollständige die Datei:
- füge eine Regel mit
setValidity()(eingeschlossen ininvisible()) hinzu, dieowner must not be emptyoderbalance must not be negativezurückgibt; - erstelle die Generics
deposit(acc, amount)undwithdraw(acc, amount)sowie derenAccount-Methoden:depositbricht bei Beträgen von 0 oder weniger mitamount must be positiveab (verwendecall. = FALSE); beide ändern den Kontostand, rufenvalidObject()auf und geben das Konto zurück; - füge eine
show()-Methode hinzu, dieada: 50und eine neue Zeile ausgibt.
Der bereitgestellte Code liest den Besitzer und anschließend Zeilen wie dep 50 oder wd 20 ein und gibt das Konto nach jeder Änderung oder refused: sowie das Problem aus.
Dein Code gehört in Account.R. main.R enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.
Probier es selbst
source("Account.R")
# Gelieferter Ein-/Ausgabe-Code: belasse ihn wie er ist
input <- suppressWarnings(readLines(file("stdin")))
acc <- tryCatch(new("Account", owner = input[1], balance = 0),
error = function(e) { cat("error: ", sub(".*object: ", "", conditionMessage(e)), "\n", sep = ""); NULL })
if (!is.null(acc)) {
for (line in input[-1]) {
p <- strsplit(line, " ")[[1]]
amount <- as.numeric(p[2])
acc <- tryCatch({
changed <- if (p[1] == "dep") deposit(acc, amount) else withdraw(acc, amount)
print(changed)
changed
}, error = function(e) {
cat("refused: ", sub(".*object: ", "", conditionMessage(e)), "\n", sep = "")
acc
})
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1S3-Grundlagen
Arbeiten mit DateienListen mit einer KlasseKonstruktorfunktionenPrint- und Format-MethodenValidatoren und HilfsfunktionenRückblick – Temperaturen4Kapselung in R
Closures als ObjekteUmgebungen als ObjekteZugriffsfunktionenZustand schützenRückblick – Parkuhr2S3-Generics und Methoden
Generics und UseMethodStandardmethodenMethoden für Basis-GenericsGrundlagen von NextMethodWiederholung – Flächeninhalte von Formen5S4-Klassen
S4-Klassen definierenSlots und ZugriffGültigkeitsprüfungenShow-MethodenRückblick – Produktkatalog8Referenzklassen
Referenzklassen definierenMethoden und FeldänderungenKopiersemantikVererbung und callSuperZusammenfassung – Aufgabenwarteschlange11Projekt: Bibliotheksverwaltung
Bücher und MitgliederBücher ausleihen3S3-Vererbung
KlassenvektorenReihenfolge der MethodensucheErweitern mit NextMethodPrüfen mit inherits()Rückblick – Mitarbeiterabrechnung6S4 Generics und Methoden
Generics erstellenMethoden für eingebaute FunktionenDispatch für zwei ArgumentesetValidity und validObjectRückblick – Geld und WährungÜbe selbstständig: Online-R-Compiler