Menu
CoddyTech

ジェネリックと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()(半径の2乗に円周率を掛けた値)を、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オンラインコンパイラ