8

2つのクラス(aと)があり、それらのメソッドbを定義したいと思います。+2つのクラスの4つの可能な組み合わせに対して、異なるメソッドが必要です。

a + a  method 1
a + b  method 2
b + a  method 3
b + b  method 4

S4を多重ディスパッチに使用できることは知っていますが、S3を使用してこの動作をエミュレートする方法があるかどうかを知りたいです。私のアプローチは次のとおりです。

a <- "b"
class(a) <- "a"

b <- "e"
class(b) <- "b"

Ops.a <- function(e1, e2){
  if (class(e1) == "a" &
      class(e2) == "a")
    print("a & a")
  if (class(e1) == "a" &
        class(e2) == "b")
    print("a & b")
  if (class(e1) == "b" &
        class(e2) == "a")
    print("b & a")
  NULL
}

a + a
a + b
b + a

これはすべて正常に機能しますが、もちろん次のことは定義されていません。

b + b

この場合をカバーするために、別のメソッド定義を追加しました。

Ops.b <- function(e1, e2){
  if (class(e1) == "b" &
        class(e2) == "b")
    print("b & b")
  NULL
}

これによりb + b動作しますが、現在a + bb + aメソッドに一貫性がなく、エラーが発生します。

> a + b
error in a + b : non-numeric argument for binary operator
additional: warning:
incompatible methods ("Ops.a", "Ops.b") for "+"

S3を使用して4つのケースすべてを適切に定義する方法はありますか?

4

3 に答える 3

11

同じ関数として+.aとを定義することでそれを行うことができます。+.b例えば:

a <- "a"
class(a) <- "a"
b <- "b"
class(b) <- "b"

`+.a` <- function(e1, e2){
  paste(class(e1), "+", class(e2))
}
`+.b` <- `+.a`

a+a
# [1] "a + a"
a+b
# [1] "a + b"
b+a
# [1] "b + a"
b+b
# [1] "b + b"

# Other operators won't work
a-a
# Error in a - a : non-numeric argument to binary operator

とを定義するOps.aと、他の演算子の操作も定義されます。これらの演算子には、次の関数Ops.bでアクセスできます。.Generic

##### Start a new R session so that previous stuff doesn't interfere ####
a <- "a"
class(a) <- "a"
b <- "b"
class(b) <- "b"

Ops.a <- function(e1, e2){
  paste(class(e1), .Generic, class(e2))
}

Ops.b <- Ops.a

a+a
# [1] "a + a"
a+b
# [1] "a + b"
b+a
# [1] "b + a"
b+b
# [1] "b + b"


# Ops covers other operators besides +
a-a
# [1] "a - a"
a*b
# [1] "a * b"
b/b
# [1] "b / b"

更新:これで遊んでいる間に私が発見したもう1つのこと。これをパッケージに入れると、「数値以外の引数」エラーと「互換性のない演算子」の警告が表示されます。これは、複数の演算子がまったく同じオブジェクトであり、メモリ内のアドレスが同じである場合にのみ、Rが問題ないためです。ただし、パッケージの構築と読み込みでは、2つの関数がこの正確なIDを失います。(これはを使用して確認できますpryr::address()

私が見つけた1つのことは、パッケージがロードされたときにS3メソッドを明示的に登録することです。たとえば、これはパッケージ内に含まれます。

# Shows the classes of the two objects that are passed in
showclasses <- function(e1, e2) {
  paste(class(e1), "+", class(e2))
}    

.onLoad <- function(libname, pkgname) {
  registerS3method("+", "a", showclasses)
  registerS3method("+", "b", showclasses)
}

この場合、2つのメソッドはメモリ内のまったく同じオブジェクトを指し、それは機能します(少しハックですが)。

于 2014-08-29T20:07:40.443 に答える
4

まあ、あなたはその戦略を使うことはできません。ヘルプ(操作)ページでそのように発見して文書化したため、特に禁止されています。

「一方の引数でメソッドが見つかった場合、または両方で同じメソッドが見つかった場合は、そのメソッドが使用されます。異なるメソッドが見つかった場合は、「互換性のないメソッド」に関する警告が表示されます。その場合、またはどちらのメソッドも見つからなかった場合引数は内部メソッドが使用されます。」

したがって、すべてのケースを同じメソッドに入れる必要があります。(テスト済みで成功します。)

于 2012-12-01T16:11:31.740 に答える
2

引数を逆にして演算子を呼び出すのはどうですか?

Ops.b <- function(e1, e2){
  if (class(e1) == "b" &
        class(e2) == "b")
    print("b & b")
  if (class(e1) =="b" & class(e2)=="a")
    e2+e1
  NULL
}

ただし、適切な多重ディスパッチを使用することを強くお勧めします。したがって、これにはS4を使用することをお勧めします。S4メソッドとS3メソッドを1つの関数に結合すること、 および S4ディスパッチをベースRS3ジェネリックに追加するを参照してください 。

于 2012-12-01T12:55:53.060 に答える