5

R でのオブジェクト指向プログラミングは初めてで、オブジェクトを変更する関数を適切に記述する方法に苦労しています。

この例は機能します:

store1 <- list(
  apples=3,
  pears=4,
  fruits=7
)
class(store1) <- "fruitstore"
print.fruitstore <- function(x) {
  paste(x$apples, "apples and", x$pears, "pears", sep=" ")
}
print(store1)
addApples <- function(x, i) {
x$apples <- x$apples + i
x$fruits <- x$apples + x$pears
return(x)
}
store1 <- addApples(store1, 5)
print(store1)

しかし、オブジェクト全体を返さずにこれを行うためのよりクリーンな方法があるはずです。

addApples(store1, 5)  # Preferable line...
store1 <- addApples(store1, 5)  # ...instead of this line

Rで変更関数を書く適切な方法は何ですか? 「<<-」?

更新: R で OOP のロゼッタ ストーンになったものに感謝します。非常に有益です。私が解決しようとしている問題は、フローの点で非常に複雑であるため、参照クラスの厳格さが構造を助けてくれる可能性があります。1つだけではなく、すべての回答を回答として受け入れることができれば幸いです。

4

4 に答える 4

4

参照クラスへの飛び込みを節約したい場合は、置換関数を使用して S3 クラスで実際にこれを行うことができます。まず、あなたの例

store1 <- list(apples=3,pears=4)
class(store1) <- "fruitstore"
print.fruitstore <- function(x) {
  x <- paste(unlist(store1), names(store1), collapse=", ")
  x <- paste0(x, " for a total of ", sum(unlist(store1)), " fruit.")
  NextMethod()
}
store1
# [1] "3 apples, 4 pears for a total of 7 fruit."

NextMethodを使用するということは、を実行する必要がなくprint(store1)、入力するだけでよいことに注目してくださいstorex基本的に、画面に表示したいものに再割り当てしたら、デフォルトのprintメソッドをディスパッチするだけです。それで:

`addapples<-` <- function(x, ...) UseMethod("addapples<-")
`addapples<-.fruitstore` <- function(x, value) {
  x[["apples"]] <- x[["apples"]] + value
  x
}
addapples(store1) <- 4
store1
# [1] "7 apples, 4 pears for a total of 11 fruit."

多田!繰り返しますが、実際には典型的な S3 の使用例ではありません。[および関数を除いて[[、置換関数は通常、属性 (クラス、長さなど) を更新することを目的としていますが、それを拡張してもそれほど害はないと思います。

これは参照代入による実数ではないことに注意してください。実際、fruitstoreオブジェクトはコピーされ、変更され、元の変数に再割り当てされます ( R Docsを参照)。

于 2014-01-20T21:56:43.650 に答える
3

data.table パッケージを見てください。

パッケージをロードします。library(data.table)

data.table オブジェクトを定義します。

store1 <- data.table(apples=3,
                 pears=4,
                 fruits=7)

次に関数 addApple を定義します。

 addApple <- function(data,i) {data[,apples:=(data[1,apples]+i)]; 
                             data[,fruits:=(data[1,apples]+data[1,pears])]}

それでおしまい。

書くaddApple(store1)と +i リンゴと「リンゴ + ナシ」の果物が得られるはずです。

また、必要に応じて S3 クラスを定義することもできますが、それが data.frame および data.table クラスを継承していることを確認してください。

class(store1) <- c("fruitstore",class(store1))

もう 1 つ、print を明示的にして、S3 メソッドを print 用に書き直す必要があります。

print.fruitstore <- function(x) {
   print(paste(x$apples, "apples and", x$pears, "pears", sep=" "))
}

または、単に cat を使用することもできます:

print.fruitstore <- function(x) {cat(x$apples, "apples and", x$pears, "pears")}
于 2014-01-20T21:10:11.873 に答える
3

コメントの1つで提案されているように、これは参照クラスの実装です。Stores基本的な考え方は、 3 つのフィールドを持つと呼ばれる参照クラスをセットアップすることです: applespearsおよびfruits(アクセサ メソッドになるように編集)。initializeメソッドは新しいストアを初期化するために使用され、メソッドaddApplesはリンゴをストアに追加しますが、メソッドは他のオブジェクトshowと同等です。print

Stores = setRefClass("Stores", 
  fields = list(
    apples = "numeric",
    pears  = "numeric",
    fruits = function(){apples + pears}
  ), 
  methods = list(
    initialize = function(apples, pears){
      apples <<- apples
      pears <<- pears
    },
    addApples = function(i){
      apples <<- apples + i
    },
    show = function(){
      cat(apples, "apples and", pears, "pears")
    }
  )
)

新しいストアを初期化して呼び出すと、次のようになります

FruitStore = Stores$new(apples = 3, pears = 4)
FruitStore

# 3 apples and 4 pears

次に、メソッドを呼び出してaddApples、ストアに 4 つのリンゴを追加します。

FruitStore$addApples(4)
FruitStore

# 7 apples and 4 pears

編集。ハドリーの提案に従って、回答を更新してfruits、アクセサー メソッドになりました。applesストアに追加するたびに更新されます。ありがとう@hadley。

于 2014-01-20T21:33:24.040 に答える
2

これは、オブジェクトとクラスをプロトタイプの単一の概念に統合するproto パッケージを使用した実装です。Fruitstoreたとえば、ここでは、どちらがクラスの役割を果たすオブジェクトであり、store1どちらが特定のストアの役割を果たすオブジェクトであるかに違いはありません。どちらもプロト オブジェクトです。

library(proto)

Fruitstore <- proto(
    addApples = function(., i) {
        .$apples <- .$apples + i
        .$fruits <- .$apples + .$pears
    },
    print = function(.) cat(.$apples, "apples and", .$pears, "pears\n")
)

# define store1 as a child of Fruitstore
store1 <- Fruitstore$proto(apples = 3, pears = 4, fruits = 7)

store1$addApples(5)
store1$print()
于 2014-01-20T22:07:06.350 に答える