4

実際の質問

  1. R6クラスが (非公式の S3) クラスから継承するという事実R6は、まさにそのクラスの署名引数の S4 メソッドの定義を許可するべきではないでしょうか?

  2. これは - AFAICT - そうではないので、現在の S3/S4 標準に沿った回避策、またはそのような状況で「ベストプラクティス」と見なすことができる回避策は何ですか?

背景と例

参照クラス

参照クラスのすべてのインスタンスが継承するスーパークラス ( envRefClass)でディスパッチするメソッドを定義する次の例を考えてみます。

TestRefClass <- setRefClass("TestRefClass", fields= list(.x = "numeric"))
setGeneric("foo", signature = "x",
  def = function(x) standardGeneric("foo")
)
setMethod("foo", c(x = "envRefClass"),
  definition = function(x) {
    "I'm the method for `envRefClass`"
})
> try(foo(x = TestRefClass$new()))
[1] "I'm the method for `envRefClass`"

class()この継承構造は、その事実を明らかにしないため、直接明らかではありません。

class(TestRefClass$new())
[1] "TestRefClass"
attr(,"package")
[1] ".GlobalEnv"

ただし、クラス ジェネレーター オブジェクトの属性を見ると、次のことがわかります。

> attributes(TestRefClass)
[... omitted ...]

 Reference Superclasses:  
    "envRefClass"

[... omitted ...]

だからディスパッチは機能する

R6 クラス

R6 クラスに同様のことをしたい場合、(参照クラスと比較して) 最初はそのように見えても、物事は単純ではないように見えます。

TestR6 <- R6Class("TestR6", public = list(.x = "numeric"))
setMethod("foo", c(x = "R6"),
  definition = function(x) {
    "I'm the method for `R6`"
})
> try(foo(x = TestR6$new()))
Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘foo’ for signature ‘"TestR6"’

「単純に見える」とは、すべての R6 クラスが、メソッド ディスパッチのスーパークラスとして使用できるclass()クラスから継承することを実際に示唆していることを意味します。R6

class(TestR6$new())
[1] "TestR6" "R6"  

のヘルプ ページでR6Class()は、クラスR6class = TRUE. これが、このクラスの S4 メソッドを定義しようとすると警告が表示される理由でもあります。

したがって、これにより、基本的に2つの可能なオプション/回避策が残ります。

  1. クラスR6を正式なクラスに変えるsetOldClass()
  2. R6 クラスのすべてのインスタンスを他のスーパークラスから継承させます。たとえば、.R6

広告 1)

setOldClass("R6")
> isClass("R6")
[1] TRUE

これは、クラス テーブル/グラフで S3 スタイルをハッキングするときに機能します。

dummy <- structure("something", class = "R6")
> foo(dummy)
[1] "I'm the method for `R6`"

ただし、実際の R6 クラス インスタンスでは失敗します。

> try(foo(x = TestR6$new()))
Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘foo’ for signature ‘"TestR6"’

広告 2)

.R6 <- R6Class(".R6")
TestR6_2 <- R6Class("TestR6_2", inherit = .R6, public = list(.x = "numeric"))
setMethod("foo", c(x = ".R6"),
  definition = function(x) {
    "I'm the method for `.R6`"
})
> try(foo(x = TestR6_2$new()))
Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘foo’ for signature ‘"TestR6_2"’

結論

アプローチ 1 の並べ替えは、S3 と S4 にある程度の互換性を持たせるために「グレー エリア」で動作しますが、アプローチ 2 は、IMO が機能する完全に有効な「純粋な S4」ソリューションのように見えます。Rでの非公式/公式クラスとメソッドディスパッチの相互作用に関して、R6クラスの実装に矛盾があるかどうかという問題を提起することにはならなかったという事実.

4

1 に答える 1

8

Hadley Wickham の厚意によりsetOldClass()、継承構造を含めると実際に問題が解決することがわかりました。

require("R6")
setOldClass(c("TestR6", "R6"))
TestR6 <- R6Class("TestR6", public = list(.x = "numeric"))
setGeneric("foo", signature = "x",
  def = function(x) standardGeneric("foo")
)
setMethod("foo", c(x = "R6"),
  definition = function(x) {
    "I'm the method for `R6`"
  })
try(foo(x = TestR6$new()))

ただし、AFAICT、これは、S4 メソッドを機能させたいすべてsetOldClass()の R6 クラスに対して、パッケージに対してがそのように呼び出されることを確認する必要があることを意味します。

これは、これらの呼び出しを関数.onLoad()または.onAttach()(こちらを参照)にまとめることで実行できます。

.onLoad <- function(libname, pkgname) {
  setOldClass(c("TestR6_1", "R6"))
  setOldClass(c("TestR6_2", "R6"))
  setOldClass(c("TestR6_3", "R6"))
}

TestR6_1これは、3 つの R6 クラス ( ~TestR6_3)を定義したと仮定しています。

于 2014-12-09T17:52:11.440 に答える