פונקציות בנאי
חלק מהיחידה תכנות מונחה עצמים במסלול ה-R של Coddy. שיעור 3 מתוך 57.
מכיוון ש־R אף פעם לא בודקת את השדות של אובייקט S3, כל אובייקט ממחלקה צריך להיבנות באמצעות פונקציה אחת, הבנאי שלו. לפי המוסכמה, הבנאי ברמה הנמוכה נקרא new_ בתוספת שם המחלקה. הוא מקבל כל שדה כארגומנט, בודק את הטיפוסים באמצעות stopifnot() ומחזיר את האובייקט:
new_money <- function(amount = double(), currency = character()) {
stopifnot(is.double(amount), is.character(currency))
structure(list(amount = amount, currency = currency), class = "money")
}
m <- new_money(9.5, "EUR")
cat(m$amount, m$currency)פלט:
9.5 EURstopifnot() עוצרת את התוכנית כאשר אחד התנאים שלה אינו TRUE, וההודעה מציינת איזו בדיקה נכשלה. סוג שגוי מזוהה ברגע שהאובייקט נבנה, ולא מאוחר יותר, כשפונקציה אחרת משתמשת בו:
new_money <- function(amount = double(), currency = character()) {
stopifnot(is.double(amount), is.character(currency))
structure(list(amount = amount, currency = currency), class = "money")
}
m <- new_money("9.5", "EUR")פלט:
Error in new_money("9.5", "EUR") : is.double(amount) is not TRUE
Calls: new_money -> stopifnot
Execution haltedהבנאי הוא המקום היחיד שבו קוראים ל־structure() עבור המחלקה, כך שלכל האובייקטים יש אותם שדות באותו סדר. קוד שזקוק לאובייקטים רבים קורא לו בתוך לולאה או עם lapply():
new_point <- function(x = 0, y = 0) {
stopifnot(is.numeric(x), is.numeric(y))
structure(list(x = x, y = y), class = "point")
}
pts <- lapply(1:3, function(i) new_point(i, i * i))
for (p in pts) cat(p$x, p$y, "\n")פלט:
1 1
2 4
3 9 אובייקט S3 לא חייב להיות רשימה. בנאי יכול לתת תווית לווקטור פשוט ולאחסן עובדות נוספות כתכונות, וכך פועלים הגורמים והתאריכים של R עצמו. attr(x, "unit") קוראת תכונה:
new_distance <- function(value = double(), unit = "m") {
stopifnot(is.double(value), is.character(unit))
structure(value, unit = unit, class = "distance")
}
d <- new_distance(c(1.5, 2), "km")
print(attr(d, "unit"))
print(unclass(d)[2] * 1000)פלט:
[1] "km"
[1] 2000אתגר
קלהשלימו את הבנאי new_book(title, author, pages) בקובץ Book.R. הוא בודק באמצעות stopifnot() ש-title ו-author הם מסוג תו וש-pages הוא מספרי, ומחזיר רשימה עם שלושת השדות האלה והמחלקה book. לאחר מכן השלימו את book_label(b), שמחזירה Dune by Frank Herbert, 412 pages.
הקוד שסופק קורא שורות בפורמט title;author;pages. הוא מעביר את pages כמספר אם הוא מורכב מספרות, וכטקסט המקורי אחרת, מדפיס book: ואת התווית עבור כל אובייקט, ומדפיס rejected: ואת הכותרת כשהבנאי נעצר.
הקוד שלכם צריך להיכתב בקובץ Book.R. הקובץ main.R מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.
נסו בעצמכם
source("Book.R")
# קוד קלט/פלט שסופק: יש להשאיר אותו כפי שהוא
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
p <- strsplit(line, ";")[[1]]
pages <- if (grepl("^[0-9]+$", p[3])) as.numeric(p[3]) else p[3]
b <- tryCatch(new_book(p[1], p[2], pages), error = function(e) NULL)
if (is.null(b)) {
cat("rejected: ", p[1], "\n", sep = "")
} else {
cat(class(b), ": ", book_label(b), "\n", sep = "")
}
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות S3
עבודה עם קבציםרשימות עם מחלקהפונקציות בנאימתודות print ו-formatמאמתים ופונקציות עזרחזרה – טמפרטורותתרגלו בעצמכם: קומפיילר R אונליין