ジェネリックとUseMethod
CoddyのRジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 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"呼び出し側は1つの呼び出しを書き、各オブジェクトはそれぞれ独自の方法で応答します。これがポリモーフィズムです。混在したリストをループするコードでは、クラスごとに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() の後のコードは実行されません。したがって、ジェネリックは 1 行です。
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チャレンジ
簡単図形は3つのファイルに分割されています。Shape.Rにある汎用のarea()を完成させ、次にCircle.Rでarea.circle()(半径の二乗に pi を掛けた値)、Rectangle.Rでarea.rectangle()(幅に高さを掛けた値)のメソッドを追加してください。コンストラクターは用意されています。
用意されたコードはcircle 2またはrectangle 3 4の行を読み込み、各図形のクラスと面積を小数点以下2桁で出力します: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 = "")
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rオンラインコンパイラ