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 refusedvalidObject(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"אתגר
קללמחלקה 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
})
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות S3
עבודה עם קבציםרשימות עם מחלקהפונקציות בנאימתודות print ו-formatמאמתים ופונקציות עזרחזרה – טמפרטורות3ירושה ב-S3
וקטורי מחלקהסדר חיפוש המתודותהרחבה באמצעות NextMethodבדיקה באמצעות inherits()סיכום – שכר העובדים6S4: גנריות ומתודות
יצירת גנריותמתודות עבור רכיבים מובניםבחירת מתודה לפי שני ארגומנטיםsetValidity ו-validObjectסיכום – כסף ומטבעתרגלו בעצמכם: קומפיילר R אונליין