Menu
CoddyTech

Hàm generic và UseMethod

Thuộc phần Lập trình hướng đối tượng trong hành trình R của Coddy. Bài học 7 trên 57.

Một hàm tổng quát là hàm có phần thân là UseMethod("name"). Khi được gọi, hàm sẽ xem lớp của đối số đầu tiên rồi gọi phương thức name.class. print() và format() hoạt động theo cách này; bạn cũng có thể tự viết hàm tổng quát:

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

Đầu ra:

[1] "Rex says woof"
[1] "Tom says meow"

Bên gọi viết một lời gọi, rồi mỗi đối tượng trả lời theo cách riêng. Đây là tính đa hình: đoạn code duyệt qua một danh sách hỗn hợp không cần có if cho từng lớp:

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

Đầu ra:

Rex says woof 
Tom says meow 
Bo says woof 

Một phương thức nhận các đối số của hàm generic. Thêm tham số ... vào hàm generic để các phương thức có thể nhận thêm đối số riêng, chẳng hạn như digits cho hình tròn:

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

Đầu ra:

[1] 9
[1] 3.14
[1] 3.1416

UseMethod() chuyển lời gọi cho phương thức và không bao giờ quay lại: giá trị mà phương thức trả về chính là kết quả của hàm tổng quát, còn code phía sau UseMethod() sẽ không chạy. Vì vậy, hàm tổng quát chỉ gồm một dòng:

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

Đầu ra:

[1] 10
challenge icon

Thử thách

Dễ

Các hình được chia thành ba tệp. Hoàn thiện hàm tổng quát area() trong Shape.R, rồi thêm các phương thức area.circle() (pi nhân với bán kính bình phương) trong Circle.R và area.rectangle() (chiều rộng nhân với chiều cao) trong Rectangle.R. Các hàm khởi tạo đã được cung cấp.

Code được cung cấp đọc các dòng circle 2 hoặc rectangle 3 4 và in ra lớp cùng diện tích của mỗi hình với hai chữ số thập phân: circle: 12.57.

Code của bạn nằm trong Shape.R, Circle.R và Rectangle.R. main.R chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.

Tự làm thử

source("Shape.R")
source("Circle.R")
source("Rectangle.R")

# Đoạn code đầu vào/đầu ra đã được cung cấp: giữ nguyên
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 = "")
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch R online