2014-01-20 66 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() 
相关问题