Menu
Coddy logo textTech

ジェネリックと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.1416

UseMethod() は呼び出しをメソッドに渡し、戻ってくることはありません。メソッドが返す値がジェネリックの結果となり、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
challenge icon

チャレンジ

簡単

図形は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 = "")
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Rオンラインコンパイラ