階級制度
R、S3、S4、および参照クラスの 3 つのクラス システムを確認してください。
## S3 methods, Section 5 of
RShowDoc("R-lang")
## S4 classes
?Classes
?Methods
## Reference classes
?ReferenceClasses
Java のバックグラウンドを持っていると、参照クラスを使いたくなるかもしれませんが、これらには「参照セマンティクス」と遠く離れたアクションがあります (1 つのオブジェクトを変更すると、同じデータを参照する別のオブジェクトが変更されます)。一方、ほとんどの R ユーザーは「変更時にコピー」を期待します。 ' セマンティクス。S3 クラスで大きな進歩を遂げることができますが、より規律あるアプローチでは S4 を採用すると思います。S4 の機能に驚くかもしれません。その理由の 1 つは、クラス システムが Java よりも一般的な Lisp オブジェクト システムに近いからです。
他の意見やオプションがあります。
基本的な実装
「ProcessData」の設計目標が何であるかはよくわかりません。あなたの 2 つのクラスをクラス、ジェネリック、および MyClass クラスで動作するジェネリックのメソッドとして実装します。
## definition and 'low-level' constructor
.MyClass <- setClass("MyClass", representation(word="character"))
## definition of a generic
setGeneric("processData", function(x, ...) standardGeneric("processData"))
setMethod("processData", "MyClass", function(x, ...) {
cat("processData(MyClass) =", x@word, "\n")
})
これは完全で完全に機能します
> myClass <- .MyClass(word="hello world")
> processData(myClass)
processData(MyClass) = hello world
3 つのコード行は、"AllGenerics.R" と "MyClass.R" (メソッドを含む) の 2 つのファイル、または "AllGenerics.R"、"AllClasses.R"、"processData-methods.R" (メソッドはジェネリックに関連付けられており、クラスでディスパッチされていることに注意してください)。
追加実装
通常、よりユーザーフレンドリーなコンストラクターを追加します。たとえば、予想されるデータ型についてユーザーにヒントを提供したり、複雑な引数の初期化手順を実行したりします。
MyClass <- function(word=character(), ...)
{
.MyClass(word=word, ...)
}
通常、直接スロット アクセスではなく、スロット アクセサーが必要です。これは、単純な関数 (図のように) または一般的な + メソッドにすることができます。
word <- function(x, ...) x@word
スロットを更新する場合は、代わりの関数またはメソッドを記述します。関数またはメソッドには、通常、更新するオブジェクト、可能な追加の引数、およびオブジェクトを更新する値の 3 つの引数があります。これは一般的な + メソッドの実装です
setGeneric("word<-", function(x, ..., value) standardGeneric("word<-"))
setReplaceMethod("word", c("MyClass", "character"), function(x, ..., value) {
## note double dispatch on x=MyClass, value=character
x@word <- value
x
})
ややトリッキーな代替実装は
setReplaceMethod("word", c("MyClass", "character"), function(x, ..., value) {
initialize(x, word=value)
})
これは、initialize
ジェネリックおよびデフォルト メソッドをコピー コンストラクターとして使用します。これは、複数のスロットを同時に更新する場合に効率的です。
getGeneric("show")
クラスはユーザーに表示されるため、ジェネリック ( ) が既に存在する'show' メソッドを使用して、ユーザー フレンドリーな方法でクラスを表示する必要があります。
setMethod("show", "MyClass", function(object) {
cat("class:", class(object), "\n")
cat("word:", word(object), "\n")
})
そして今、私たちのユーザーセッションは次のようになります
> myClass
class: MyClass
word: hello world
> word(myClass)
[1] "hello world"
> word(myClass) <- "goodbye world"
> processData(myClass)
processData(MyClass) = goodbye world
効率
R はベクトルに対して効率的に機能します。S4 クラスも例外ではありません。そのため、クラスの各スロットは、1 つの行の要素ではなく、多くの行にまたがる列を表すように設計されています。スロット「単語」には、通常、長さが 1 よりもはるかに大きいベクトルが含まれており、操作がベクトルのすべての要素に作用することが予想されます。したがって、これを念頭に置いてメソッドを作成します。たとえば、show メソッドを次のように変更します。
setMethod("show", "MyClass", function(object) {
cat("class:", class(object), "\n")
cat("word() length:", length(word(object)), "\n")
})
これはより大きなデータオブジェクトです(私のLinuxシステムのファイルを使用)
> amer <- MyClass(readLines("/usr/share/dict/american-english"))
> brit <- MyClass(readLines("/usr/share/dict/british-english"))
> amer
class: MyClass
word() length: 99171
> brit
class: MyClass
word() length: 99156
> sum(word(amer) %in% word(brit))
[1] 97423
> amer_uc <- amer ## no copy, but marked to be copied if either changed
> word(amer_uc) <- toupper(word(amer_uc)) ## two distinct objects
これらはすべて非常に高性能です。
参照クラス「アクション・アット・ア・ディスタンス」のハザード
S4 クラスのより単純な実装に戻りましょう。直接スロット アクセスがあり、手の込んだコンストラクターはありません。これがアメリカの辞書と大文字に変換されたコピーです
.MyClass <- setClass("MyClass", representation(word="character"))
amer <- .MyClass(word=readLines("/usr/share/dict/american-english"))
amer_uc <- amer
amer_uc@word <- toupper(amer_uc@word)
大文字であることに注意してamer_uc
くださいamer
。
> amer@word[99 + 1:10]
[1] "Adana" "Adar" "Adar's" "Addams" "Adderley"
[6] "Adderley's" "Addie" "Addie's" "Addison" "Adela"
> amer_uc@word[99 + 1:10]
[1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY"
[6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"
これは実際に R ユーザーが期待していることです。別のオブジェクトを作成して変更しました。元のオブジェクトは変更されていません。これは私の主張です。Rユーザーが何を期待しているのか、私にはわからないかもしれません。R ユーザーは、これが参照クラスであるという事実にあまり注意を払っていないと思いますが、integer()
ベクトルや のような別の R オブジェクト、data.frame
または の戻り値であると考えていますlm()
。
対照的に、参照クラスの最小限の実装と同様の操作を次に示します。
.MyRefClass <- setRefClass("MyRefClass", fields = list(word="character"))
amer <- .MyRefClass(word=readLines("/usr/share/dict/american-english"))
amer_uc <- amer
amer_uc$word <- toupper(amer_uc$word)
しかし、今ではamer
とamer_uc
!の両方を変更しました。C または Java プログラマーは完全に期待していますが、R ユーザーは期待していません。
> amer$word[99 + 1:10]
[1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY"
[6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"
> amer_uc$word[99 + 1:10]
[1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY"
[6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"