גנריות ו־UseMethod
חלק מהיחידה תכנות מונחה עצמים במסלול ה-R של Coddy. שיעור 7 מתוך 57.
גנרית היא פונקציה שגוף הפונקציה שלה הוא UseMethod("name"). כשקוראים לה, היא בודקת את המחלקה של הארגומנט הראשון שלה וקוראת למתודה name.class. הפונקציות print() ו־format() פועלות כך; אפשר לכתוב גם גנריות משלך:
speak <- function(x) UseMethod("speak")
speak.dog <- function(x) paste(x$name, "says woof")
speak.cat <- function(x) paste(x$name, "says meow")
rex <- structure(list(name = "Rex"), class = "dog")
tom <- structure(list(name = "Tom"), class = "cat")
print(speak(rex))
print(speak(tom))פלט:
[1] "Rex says woof"
[1] "Tom says meow"הקורא מבצע קריאה אחת, וכל אובייקט מגיב בדרכו שלו. זהו פולימורפיזם: קוד שעובר בלולאה על רשימה מעורבת אינו זקוק ל־if עבור כל מחלקה:
speak <- function(x) UseMethod("speak")
speak.dog <- function(x) paste(x$name, "says woof")
speak.cat <- function(x) paste(x$name, "says meow")
pets <- list(structure(list(name = "Rex"), class = "dog"),
structure(list(name = "Tom"), class = "cat"),
structure(list(name = "Bo"), class = "dog"))
for (p in pets) cat(speak(p), "\n")פלט:
Rex says woof
Tom says meow
Bo says woof מתודה מקבלת את הארגומנטים של הגנרית. הוסף לגנרית פרמטר ... כדי שמתודות יוכלו לקבל ארגומנטים נוספים משלהן, למשל digits עבור מעגל:
area <- function(shape, ...) UseMethod("area")
area.square <- function(shape, ...) shape$side^2
area.circle <- function(shape, digits = 2, ...) round(pi * shape$r^2, digits)
sq <- structure(list(side = 3), class = "square")
ci <- structure(list(r = 1), class = "circle")
print(area(sq))
print(area(ci))
print(area(ci, digits = 4))פלט:
[1] 9
[1] 3.14
[1] 3.1416UseMethod() מעבירה את הקריאה אל המתודה ואינה חוזרת לעולם: כל מה שהמתודה מחזירה הוא התוצאה של הגנרית, והקוד שאחרי UseMethod() לא רץ. לכן גנרית היא שורה אחת:
size <- function(x) {
UseMethod("size")
cat("this line never runs\n")
}
size.box <- function(x) x$w * x$h
b <- structure(list(w = 2, h = 5), class = "box")
print(size(b))פלט:
[1] 10אתגר
קלהצורות מחולקות בין שלושה קבצים. השלימו את area() הגנרית ב-Shape.R, ואז הוסיפו את המתודות area.circle() (פאי כפול הרדיוס בריבוע) ב-Circle.R ואת area.rectangle() (רוחב כפול גובה) ב-Rectangle.R. הבנאים נתונים.
הקוד שסופק קורא שורות circle 2 או rectangle 3 4 ומדפיס את המחלקה והשטח של כל צורה, עם שתי ספרות אחרי הנקודה העשרונית: circle: 12.57.
הקוד שלכם צריך להופיע ב-Shape.R, ב-Circle.R וב-Rectangle.R. הקובץ main.R מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.
נסו בעצמכם
source("Shape.R")
source("Circle.R")
source("Rectangle.R")
# קוד קלט/פלט שסופק: יש להשאיר אותו כפי שהוא
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
p <- strsplit(line, " ")[[1]]
s <- if (p[1] == "circle") new_circle(as.numeric(p[2])) else new_rectangle(as.numeric(p[2]), as.numeric(p[3]))
cat(sprintf("%s: %.2f", class(s), area(s)), "\n", sep = "")
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות S3
עבודה עם קבציםרשימות עם מחלקהפונקציות בנאימתודות print ו-formatמאמתים ופונקציות עזרחזרה – טמפרטורות2גנריות ומתודות של S3
גנריות ו־UseMethodמתודות ברירת מחדלמתודות לגנריות הבסיסיסודות NextMethodסיכום – שטחי צורותתרגלו בעצמכם: קומפיילר R אונליין