Menu
Coddy logo textTech

setValidity ו-validObject

חלק מהיחידה תכנות מונחה עצמים במסלול ה-R של Coddy. שיעור 30 מתוך 57.

setValidity("Class", function(object) ...) מוסיפה את כלל התקפות לאחר הכרזת המחלקה, וכך שומרת על קריאה ארוכה ל־setClass() קצרה. בדומה ל־setGeneric(), היא מחזירה ערך שמודפס ברמה העליונה, לכן יש לעטוף אותה ב־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)

פלט:

invalid class “Account” object: balance must not be negative

שינויים צריכים להתבצע באמצעות מתודות שבודקות את התוצאה. מתודת withdraw() משנה את המשבצת, קוראת ל־validObject() ומחזירה את האובייקט רק כשהוא עדיין תקין:

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)

פלט:

refused 30

זוג גישה יכול להיות מורכב מפונקציות גנריות: balance(x) קוראת, ופונקציית החלפה גנרית "balance<-" כותבת. המתודה שלה בודקת את האובייקט, לכן balance(a) <- -5 נדחית כמו כל שינוי לא תקין אחר:

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)

פלט:

25 refused

validObject(x, test = TRUE) לא עוצרת. היא מחזירה TRUE עבור אובייקט תקין, או את טקסט הבעיה, וזה שימושי לדוח:

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

פלט:

[1] TRUE
[1] "w must be positive"
challenge icon

אתגר

קל

למחלקה Account בקובץ Account.R יש את השדות owner ו-balance. השלימו את הקובץ:

  • הוסיפו כלל באמצעות setValidity() (עטוף ב-invisible()) שמחזיר owner must not be empty או balance must not be negative;
  • צרו את הגנריקות deposit(acc, amount) ו-withdraw(acc, amount) ואת המתודות שלהן עבור Account: deposit עוצרת עם amount must be positive (השתמשו ב-call. = FALSE) עבור סכומים של 0 או פחות; שתיהן משנות את היתרה, קוראות ל-validObject() ומחזירות את החשבון;
  • הוסיפו מתודת show() שמדפיסה ada: 50 ושורה חדשה.

הקוד שסופק קורא את שם הבעלים, ואז שורות כמו dep 50 או wd 20, ומדפיס את החשבון אחרי כל שינוי או את refused: ואת הבעיה.

הקוד שלכם צריך להופיע ב-Account.R. הקובץ main.R מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.

נסו בעצמכם

source("Account.R")

# קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
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
    })
  }
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר R אונליין