setValidity i validObject
Część sekcji Programowanie obiektowe ścieżki R w Coddy. Lekcja 30 z 57.
setValidity("Class", function(object) ...) dodaje regułę poprawności po zadeklarowaniu klasy, dzięki czemu długie wywołanie setClass() pozostaje krótkie. Podobnie jak setGeneric(), zwraca wartość, która jest wyświetlana na najwyższym poziomie, więc opakuj je w invisible():
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)Wynik:
invalid class “Account” object: balance must not be negativeZmiany powinny odbywać się za pośrednictwem metod, które sprawdzają wynik. Metoda withdraw() zmienia slot, wywołuje validObject() i zwraca obiekt tylko wtedy, gdy nadal jest prawidłowy:
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)Wynik:
refused 30Para akcesorów może składać się z metod generycznych: balance(x) odczytuje wartość, a generyczna funkcja zastępująca "balance<-" ją zapisuje. Jej metoda sprawdza obiekt, więc balance(a) <- -5 zostaje odrzucone, podobnie jak każda inna nieprawidłowa zmiana:
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)Wynik:
25 refusedvalidObject(x, test = TRUE) nie przerywa działania. Zwraca TRUE dla prawidłowego obiektu albo tekst opisujący problem, co przydaje się w raporcie:
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))Wynik:
[1] TRUE
[1] "w must be positive"Wyzwanie
ŁatwyKlasa Account w pliku Account.R ma sloty owner i balance. Uzupełnij plik:
- dodaj regułę za pomocą
setValidity()(opakowaną winvisible()), która zwracaowner must not be emptylubbalance must not be negative; - utwórz generyki
deposit(acc, amount)iwithdraw(acc, amount)oraz ich metody dlaAccount:depositzgłasza błąd z komunikatemamount must be positive(użyjcall. = FALSE) dla kwot równych 0 lub mniejszych; obie metody zmieniają saldo, wywołująvalidObject()i zwracają konto; - dodaj metodę
show(), która wyświetlaada: 50i znak nowej linii.
Dostarczony kod odczytuje właściciela, a następnie wiersze dep 50 lub wd 20, po czym wyświetla konto po każdej zmianie albo refused: i opis problemu.
Umieść swój kod w pliku Account.R. Plik main.R zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
source("Account.R")
# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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
})
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy S3
Praca z plikamiListy z klasąFunkcje konstruktoraMetody print i formatWalidatory i funkcje pomocniczePowtórzenie — temperatury4Enkapsulacja w R
Domknięcia jako obiektyŚrodowiska jako obiektyFunkcje dostępoweOchrona stanuPodsumowanie – parkometr2Generics i metody S3
Generics i UseMethodMetody domyślneMetody dla podstawowych genericsPodstawy NextMethodPowtórzenie — pola figur5Klasy S4
Definiowanie klas S4Sloty i dostępSprawdzanie poprawnościMetody wyświetlaniaPodsumowanie – katalog produktów8Klasy referencyjne
Definiowanie klas referencyjnychMetody i aktualizacja pólSemantyka kopiowaniaDziedziczenie i callSuperPodsumowanie – kolejka zadań11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książek3Dziedziczenie S3
Wektory klasKolejność wyszukiwania metodRozszerzanie za pomocą NextMethodSprawdzanie za pomocą inherits()Powtórzenie — lista płac pracowników6S4: Funkcje generyczne i metody
Tworzenie funkcji generycznychMetody dla typów wbudowanychWybór metody na podstawie dwóch argumentówsetValidity i validObjectPodsumowanie — pieniądze i walutaPoćwicz samodzielnie: Kompilator R online