Ochrona stanu
Część sekcji Programowanie obiektowe ścieżki R w Coddy. Lekcja 20 z 57.
Setter to jedyne miejsce, w którym może zmienić się wartość, dlatego właśnie tutaj należy egzekwować reguły. Najpierw ją sprawdza i wywołuje stop(), zanim cokolwiek zostanie zmienione, dzięki czemu obiekt pozostaje prawidłowy:
make_account <- function(balance = 0) {
withdraw <- function(x) {
if (x <= 0) stop("amount must be positive", call. = FALSE)
if (x > balance) stop("insufficient funds", call. = FALSE)
balance <<- balance - x
}
list(withdraw = withdraw, balance = function() balance)
}
acc <- make_account(50)
msg <- tryCatch(acc$withdraw(80), error = function(e) conditionMessage(e))
cat(msg, "/", acc$balance())Wynik:
insufficient funds / 50lockBinding("rate", e) sprawia, że jedna zmienna środowiska jest tylko do odczytu. Późniejsze przypisanie do niej powoduje błąd, podczas gdy pozostałe zmienne nadal można modyfikować:
loan <- new.env()
loan$rate <- 0.05
loan$owed <- 1000
lockBinding("rate", loan)
loan$owed <- 900
msg <- tryCatch({ loan$rate <- 0; "changed" }, error = function(e) conditionMessage(e))
cat(msg, "\n")
cat(loan$rate, loan$owed, "\n")Wynik:
cannot change value of locked binding for 'rate'
0.05 900 lockEnvironment(e) uniemożliwia dodawanie nowych zmiennych. Przy bindings = TRUE blokuje też wszystkie istniejące, przez co cały obiekt staje się tylko do odczytu:
cfg <- new.env()
cfg$theme <- "dark"
lockEnvironment(cfg, bindings = TRUE)
for (step in c("change", "add")) {
msg <- tryCatch({
if (step == "change") cfg$theme <- "light" else cfg$size <- 12
"done"
}, error = function(e) conditionMessage(e))
cat(step, ":", msg, "\n")
}Wynik:
change : cannot change value of locked binding for 'theme'
add : cannot add bindings to a locked environment local() uruchamia kod w nowym środowisku i zwraca jego ostatnią wartość. Funkcje pomocnicze zdefiniowane wewnątrz pozostają ukryte, dzięki czemu plik może zachować funkcję jako prywatną, mimo że source() umieszcza wszystko inne w środowisku globalnym:
circle_area <- local({
square <- function(x) x * x
function(r) pi * square(r)
})
print(round(circle_area(2), 2))
print(exists("square"))Wynik:
[1] 12.57
[1] FALSEWyzwanie
ŁatwyUzupełnij fabrykę make_account(owner) w pliku Account.R. Saldo zaczyna się od 0, a prywatna historia zapisuje każdą zmianę jako +50 lub -20. Zwracana lista zawiera cztery funkcje:
deposit(x)kończy działanie błędemamount must be positive, chyba żexjest większe od 0;withdraw(x)kończy działanie błędemamount must be positivelubinsufficient funds;balance()zwraca saldo;history()zwraca historię jako wektor znakowy.
Sprawdzaj warunki przed wprowadzeniem jakichkolwiek zmian i używaj call. = FALSE. Dostarczony kod odczytuje właściciela, a następnie wiersze dep 50 lub wd 20, wypisuje ok albo error: wraz z komunikatem dla każdego wiersza, próbuje zmienić kopię historii oraz wypisuje saldo i historię.
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 <- make_account(input[1])
for (line in input[-1]) {
p <- strsplit(line, " ")[[1]]
amount <- as.numeric(p[2])
msg <- tryCatch({
if (p[1] == "dep") acc$deposit(amount) else acc$withdraw(amount)
"ok"
}, error = function(e) paste("error:", conditionMessage(e)))
cat(line, ": ", msg, "\n", sep = "")
}
h <- acc$history()
h[1] <- "tampered"
cat("balance: ", acc$balance(), "\n", sep = "")
cat("history: ", paste(acc$history(), collapse = ", "), "\n", sep = "")
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ówPoćwicz samodzielnie: Kompilator R online