상태 보호
Coddy R 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 20번째.
setter는 값이 변경될 수 있는 유일한 곳이므로 규칙을 적용할 곳입니다. 먼저 확인하고 무엇이든 변경되기 전에 stop()을 호출하므로 객체는 유효한 상태로 유지됩니다:
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())출력:
insufficient funds / 50lockBinding("rate", e)는 환경의 변수 하나를 읽기 전용으로 만듭니다. 그 후 해당 변수에 할당하면 오류가 발생하지만, 다른 변수들은 계속 쓸 수 있습니다:
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")출력:
cannot change value of locked binding for 'rate'
0.05 900 lockEnvironment(e)는 새 변수가 추가되는 것을 막습니다. bindings = TRUE를 사용하면 기존 변수도 모두 잠그므로 전체 객체가 읽기 전용이 됩니다:
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")
}출력:
change : cannot change value of locked binding for 'theme'
add : cannot add bindings to a locked environment local()은 새 환경에서 코드를 실행하고 마지막 값을 반환합니다. 내부에서 정의된 도우미는 숨겨진 상태로 유지되므로, source()가 그 밖의 모든 것을 전역 환경에 넣더라도 파일에서 함수를 비공개로 유지할 수 있습니다:
circle_area <- local({
square <- function(x) x * x
function(r) pi * square(r)
})
print(round(circle_area(2), 2))
print(exists("square"))출력:
[1] 12.57
[1] FALSE챌린지
쉬움make_account(owner) 팩토리를 Account.R에서 완성하세요. 잔액은 0에서 시작하며, 비공개 기록에는 +50 또는 -20과 같이 모든 변경 사항이 기록됩니다. 반환되는 리스트에는 네 개의 함수가 있습니다:
deposit(x)는x가 0보다 크지 않으면amount must be positive오류를 발생시킵니다;withdraw(x)는amount must be positive또는insufficient funds오류를 발생시킵니다;balance()는 잔액을 반환합니다;history()는 기록을 문자 벡터로 반환합니다.
무언가를 변경하기 전에 확인하고 call. = FALSE를 사용하세요. 제공된 코드는 소유자를 읽은 다음 dep 50 또는 wd 20 형식의 줄을 읽고, 각 줄에 대해 ok 또는 error: 와 해당 메시지를 출력하며, 기록의 복사본을 변경하려고 시도한 후 잔액과 기록을 출력합니다.
코드는 Account.R에 작성하세요. main.R에는 제공된 입력/출력 코드가 들어 있으며 편집할 수 없습니다.
직접 해보기
source("Account.R")
# 제공된 입력/출력 코드: 그대로 유지하세요
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 = "")
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 R 컴파일러