Menu
Coddy logo textTech

2つの引数でのディスパッチ

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

S4ジェネリックは、複数の引数のクラスによってメソッドを選択できます。シグネチャには各引数につき1つのクラスが列挙されるため、クラスの各組み合わせに独自のメソッドを持たせることができます:

setClass("Asteroid", slots = c(size = "numeric"))
setClass("Ship", slots = c(name = "character"))
invisible(setGeneric("collide", function(a, b) standardGeneric("collide")))
setMethod("collide", signature("Asteroid", "Ship"), function(a, b) paste(b@name, "is hit"))
setMethod("collide", signature("Ship", "Ship"), function(a, b) "two ships bump")
rock <- new("Asteroid", size = 3)
enterprise <- new("Ship", name = "Enterprise")
print(collide(rock, enterprise))
print(collide(enterprise, enterprise))

出力:

[1] "Enterprise is hit"
[1] "two ships bump"

"ANY" クラスはすべてのクラスにマッチします。signature("ANY", "ANY") に対するメソッドは、より具体的な組み合わせが該当しない場合に実行されるフォールバックです。

setClass("Ship", slots = c(name = "character"))
invisible(setGeneric("collide", function(a, b) standardGeneric("collide")))
setMethod("collide", signature("Ship", "Ship"), function(a, b) "two ships bump")
setMethod("collide", signature("ANY", "ANY"), function(a, b) "nothing happens")
s <- new("Ship", name = "A")
cat(collide(s, s), "/", collide(s, 42), "/", collide("x", "y"))

出力:

two ships bump / nothing happens / nothing happens

引数の順序は重要です。signature("A", "B")とsignature("B", "A")は異なるメソッドです。numericやcharacterなどの基本クラスもシグネチャに指定できます。

invisible(setGeneric("join", function(x, y) standardGeneric("join")))
setMethod("join", signature("character", "numeric"), function(x, y) paste(x, "number", y))
setMethod("join", signature("numeric", "character"), function(x, y) paste(y, "after number", x))
print(join("item", 3))
print(join(3, "item"))

出力:

[1] "item number 3"
[1] "item after number 3"

シグネチャ内のクラス "missing" は、まったく渡されなかった引数にマッチするため、1つのジェネリックで短い形式と長い形式を提供できます。

invisible(setGeneric("greet", function(name, greeting) standardGeneric("greet")))
setMethod("greet", signature("character", "missing"), function(name, greeting) paste("Hello,", name))
setMethod("greet", signature("character", "character"), function(name, greeting) paste0(greeting, ", ", name))
print(greet("Ada"))
print(greet("Ada", "Good morning"))

出力:

[1] "Hello, Ada"
[1] "Good morning, Ada"
challenge icon

チャレンジ

簡単

送料はアイテムとゾーンによって異なります。クラスは Item.R(Letter、Box。それぞれ kg スロットを持つ)と Zone.R(Domestic、Abroad。それぞれ name スロットを持つ)で宣言します。Rates.R ではジェネリック shipping(item, zone) と、各ペアに対して1つずつメソッドを作成します。

  • 自宅での Letter:1、海外では:3。
  • 自宅での Box:5に1 kgあたり2を加算、海外では:12に1 kgあたり4を加算。
  • その他のペア:no rate for this item and zone で停止します(call. = FALSE を使用)。

提供されたコードは、box home 2.5(アイテム、ゾーン、kg)のような行を読み取り、未知の単語はそのままテキストとして扱い、box to home: 10.00 または error: とメッセージを出力します。

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

自分で試してみよう

source("Item.R")
source("Zone.R")
source("Rates.R")

# 提供された入出力コード: そのままにしておく
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  kg <- as.numeric(p[3])
  item <- switch(p[1], letter = new("Letter", kg = kg), box = new("Box", kg = kg), kg)
  zone <- switch(p[2], home = new("Domestic", name = "home"), abroad = new("Abroad", name = "abroad"), p[2])
  out <- tryCatch(sprintf("%s to %s: %.2f", p[1], p[2], shipping(item, zone)),
                  error = function(e) paste("error:", conditionMessage(e)))
  cat(out, "\n", sep = "")
}
quiz icon腕試し

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

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

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