Menu
Coddy logo textTech

ジェネリックの作成

CoddyのRジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 27/57。

S4ジェネリックはsetGeneric()で作成します。その関数本体はUseMethod()に相当するS4のstandardGeneric("name")です。setGeneric()はジェネリックの名前を返し、トップレベルで呼び出すとその名前が表示されます。

setGeneric("area", function(shape) standardGeneric("area"))

出力:

[1] "area"

呼び出しを invisible() でラップすると、その行を出力から除外できます。続いて、setMethod("area", "Circle", ...) が1つのクラスに対するメソッドを追加します。

setClass("Circle", slots = c(r = "numeric"))
setClass("Square", slots = c(side = "numeric"))
invisible(setGeneric("area", function(shape) standardGeneric("area")))
setMethod("area", "Circle", function(shape) pi * shape@r^2)
setMethod("area", "Square", function(shape) shape@side^2)
cat(area(new("Circle", r = 1)), area(new("Square", side = 3)))

出力:

3.141593 9

メソッドはジェネリックと同じ引数を持ちます。ジェネリックに ... がある場合、メソッドはその後に独自の名前付き引数を追加できます。

setClass("Circle", slots = c(r = "numeric"))
invisible(setGeneric("describe", function(x, ...) standardGeneric("describe")))
setMethod("describe", "Circle", function(x, ..., digits = 2) {
  paste("circle of area", round(pi * x@r^2, digits))
})
c1 <- new("Circle", r = 2)
print(describe(c1))
print(describe(c1, digits = 4))

出力:

[1] "circle of area 12.57"
[1] "circle of area 12.5664"

setMethod() には、汎用関数がすでに存在している必要があるため、setGeneric() を含むファイルが先に読み込まれます。メソッドのないクラスに対して呼び出すと、シグネチャを示すエラーが発生して停止します。

invisible(setGeneric("area", function(shape) standardGeneric("area")))
setClass("Circle", slots = c(r = "numeric"))
setMethod("area", "Circle", function(shape) pi * shape@r^2)
area(42)

出力:

Error: unable to find an inherited method for function ‘area’ for signature ‘shape = "numeric"’
Execution halted
challenge icon

チャレンジ

簡単

図形は3つのファイルに分かれたS4クラスです。Shape.RでsetGeneric()を使い、invisible()でラップしたジェネリックarea(shape)とperimeter(shape)を作成します。Circle.RとSquare.Rではクラスを宣言し、それぞれのareaメソッドとperimeterメソッドをsetMethod()で追加します。

提供されたコードはcircle 2またはsquare 3という行を読み取り、circle: area 12.57, perimeter 12.57を出力します。

コードはShape.R、Circle.R、Square.Rに記述します。main.Rには提供された入出力コードが含まれており、編集できません。

自分で試してみよう

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

# 提供された入出力コード: そのままにしておく
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  s <- if (p[1] == "circle") new("Circle", r = as.numeric(p[2])) else new("Square", side = as.numeric(p[2]))
  cat(sprintf("%s: area %.2f, perimeter %.2f", p[1], area(s), perimeter(s)), "\n", sep = "")
}
quiz icon腕試し

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

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

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