Menu
CoddyTech

Generics i UseMethod

Część sekcji Programowanie obiektowe ścieżki R w Coddy. Lekcja 7 z 57.

Funkcja ogólna to funkcja, której ciało zawiera UseMethod("name"). Gdy zostanie wywołana, sprawdza klasę swojego pierwszego argumentu i wywołuje metodę name.class. print() i format() działają w ten sposób; możesz też napisać własne funkcje ogólne:

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

Wynik:

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

Wywołujący wykonuje jedno wywołanie, a każdy obiekt odpowiada na swój sposób. To jest polimorfizm: kod, który iteruje po mieszanej liście, nie potrzebuje instrukcji if dla każdej klasy:

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

Wynik:

Rex says woof 
Tom says meow 
Bo says woof 

Metoda przyjmuje argumenty funkcji generycznej. Dodaj do funkcji generycznej parametr ..., aby metody mogły przyjmować własne dodatkowe argumenty, takie jak digits w przypadku koła:

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

Wynik:

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

UseMethod() przekazuje wywołanie metodzie i nigdy nie wraca: cokolwiek zwróci metoda, jest wynikiem funkcji ogólnej, a kod po UseMethod() nie zostaje uruchomiony. Funkcja ogólna składa się więc z jednego wiersza:

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

Wynik:

[1] 10
challenge icon

Wyzwanie

Łatwy

Definicje kształtów są podzielone na trzy pliki. Uzupełnij generyczną funkcję area() w pliku Shape.R, a następnie dodaj metody area.circle() (pi razy kwadrat promienia) w pliku Circle.R oraz area.rectangle() (szerokość razy wysokość) w pliku Rectangle.R. Konstruktory są podane.

Dostarczony kod odczytuje wiersze circle 2 lub rectangle 3 4 i wypisuje klasę każdego kształtu oraz jego pole z dokładnością do dwóch miejsc po przecinku: circle: 12.57.

Twój kod umieść w plikach Shape.R, Circle.R i Rectangle.R. Plik main.R zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.

Spróbuj swoich sił

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

# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator R online