2014-01-20 94 views
3

我是R中的面向對象編程的新手,並且很難與正確編寫修改對象的函數編寫。修改S3對象而不返回它?

這個例子的工作原理:

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羅塞塔石。非常豐富。 我試圖解決的問題在流程方面非常複雜,因此參考類的剛性可能會帶來結構的幫助。我希望我能夠接受所有答案作爲答案,而不僅僅是答案。

+6

如果你真的想修改位置,那麼也許你不應該使用S3對象,而是[引用類](http://adv-r.had.co.nz/OO-essentials。 html#rc)對象。 – Andrie

+0

好評。參考類的目的突然有意義。 – Chris

回答

3

下面是引用類

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

一件事,你應該做的打印明確的重寫你的S3方法打印如其中一條評論所建議的那樣。基本思想是建立一個名爲Stores的參考類,該類有三個字段:​​,pearsfruits(編輯爲訪問方法)。 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 

編輯。根據Hadley的建議,我已經更新了我的答案,以便fruits現在成爲訪問方法。它會保持更新,因爲我們將更多​​添加到商店。謝謝@hadley。

+2

似乎'水果'不應該是一個領域,但一個計算方法(或者如果你想成爲一個真正的幻想,你可以做一個訪問器的方法,所以'FruitStore $ fruit'仍然可以工作) – hadley

+0

沒錯。我使用訪問器方法更新了我的答案。 – Ramnath

2

你應該看看data.table package

負載軟件包: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)你應該+我蘋果和「蘋果+梨」水果。

如果你願意,你仍然可以定義你的S3類,只要確保它繼承了data.frame和data.table類即可。

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

或者你可以只用貓:

print.fruitstore <- function(x) {cat(x$apples, "apples and", x$pears, "pears")} 
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),我只需鍵入store。基本上,一旦我重新分配x就是我想要在屏幕上顯示的內容,我只是發送默認的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)。

2

這是一個使用proto package的實現,它將對象和類統一爲原型的單一概念。例如,這裏的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() 
相關問題