2013-04-20 92 views
5

在像java这样的常见编程语言中,每个文件通常对应一个类。r项目的文件和目录结构

我刚开始与R.我想建一个小程序,我想创建一个特定的文件和目录结构像这样

Main.R # the main control script 
MyClass.R # A class that is referenced from within Main.R 
ProcessData.R # Another class that uses an object of MyClass.R as input 

所以我想要做的像这样(伪代码):

Main.R

myc <- new MyClass # create a new instance of MyClass from within Main.R 
pd <- new ProcessData 
pd$processMyClass(myc) # call a method in ProcessData that processes the myc object in some way 

所以这是比较抽象的,但我只是想知道,如果这是在原则上可能的R.

更新:我需要更具体。因此,问题是:如何通过保持以下玩具程序的相同数量的文件和结构来将下列java程序翻译成R程序?

Main.java:

public static void main(String[] args) { 
    MyClass myc = new MyClass("SampleWord"); 
    ProcessData pd = new ProcessData(); 
    pd.processData(myc); 
} 

MyClass.java

class MyClass { 

    public String word; 

    public MyClass(String word) { 
     this.word = word; 
    } 
} 

ProcessData.java

class ProcessData.java { 

    public void processData(MyClass myc) { 
     System.out.println("pd.processData = " + myc.word); 
    } 

} 
+1

这是可能的吗?参考?如果这是个问题,当然可以将你的程序的不同部分放在不同的文件中。如果这是个问题,参考类就像伪代码一样工作。请参阅'?setRefClass'。 – 2013-04-20 13:24:13

+0

请注意,面向对象的编程风格并非R最自然的东西,它具有更多的编程风格。 – 2013-04-20 15:23:38

+0

面向对象程序设计对R来说是非常不自然的,它已经实现了大约四五次,而功能风格的程序设计只用了一次S语言本身,并且从那以后几乎没有改变。 – Spacedman 2013-04-20 17:07:12

回答

7

引用类下面我们试图尽可能接近的方式,使用R,我们可以复制Java代码的问题。在这三个R类系统(S3,S4,Reference Classes)中建立的参考类似乎最接近这种风格。参考类是最近要添加到R中的类系统,它的快速推广可能是由于熟悉该类型的Java程序员来到R的。

(如果你创建一个包出来的这则忽略的所有语句)

Main.R文件:

source("MyClass.R") 
source("ProcessData.R") 

main <- function() { 
    myc <- new("MyClass", word = "SampleWord") 
    pd <- new("ProcessData") 
    cat("pd$processData =", pd$processData(myc), "\n") 
} 

MyClass.R文件:

setRefClass("MyClass", 
    fields = list(word = "character") 
) 

过程数据.R文件:

setRefClass("ProcessData", 
    fields = list(myc = "MyClass"), 
    methods = list(
     processData = function(myc) myc$word 
    ) 
) 

要运行:

source("Main.R") 
main() 

原包proto package实现一个最先与自编程语言和存在在一定程度上在JavaScript,Lua和特别的io language基础面向对象的编程的原型模型。原可以容易地模拟这种风格(如在proto vignette的性状部分中讨论的):

Main.R文件:

source("MyClass.R") 
source("ProcessData.R") 

library(proto) 

main <- function() { 
    myc <- MyClass$new("SampleWord") 
    pd <- ProcessData$new() 
    cat("pd$processData =", pd$processData(myc), "\n") 
} 

MyClass.R文件:

MyClass <- proto(
    new = function(., word) proto(word = word) 
) 

ProcessData.R文件:

ProcessData <- proto(
    new = function(.) proto(.), 
    processData = function(., myc) myc$word 
) 

运行:

source("Main.R") 
main() 

更新:增加了proto的例子。

更新2:改进参考类示例中的mainMyClass

+0

谢谢!工作正常。 – toom 2013-04-21 13:27:58

10

类系统

查核在R中的三级系统, S3,S4和参考ce类。

## S3 methods, Section 5 of 
RShowDoc("R-lang") 

## S4 classes 
?Classes 
?Methods 

## Reference classes 
?ReferenceClasses 

随着你会被诱惑去参考类的Java背景,但是这些“引用语义”和超距作用(改变一个对象改变另一种是指相同的数据),而最R用户期望'copy on change'语义。一个人可以在S3班上取得很大的进步,但是我认为采取更严谨的方法会采用S4。 S4的特点会让你大吃一惊,部分原因是类系统更接近于普通的lisp对象系统而不是java。

还有other opinions and options

基本实现

我真的不知道你的设计目标与'过程数据”是什么;我将把你的两个类实现为一个类,一个泛型和一个在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) =", [email protected], "\n") 
}) 

这是完整的,功能齐全的

> myClass <- .MyClass(word="hello world") 
> processData(myClass) 
processData(MyClass) = hello world 

三个代码行可能被放置在两个文件中,“AllGenerics.R”和“MyClass.R”(包括法)或三个文件“AllGenerics.R”,“AllClasses.R”,“processData-methods.R”(注意方法与泛型相关联,并在类上派发)。

其他实施

人们通常会增加一个更人性化的构造函数,例如提供提示给用户关于预期的数据类型或进行复杂的参数初始化步骤

MyClass <- function(word=character(), ...) 
{ 
    .MyClass(word=word, ...) 
} 

通常一个想要一个插槽accesssor,而不是直接插槽访问。这可以是一个简单的函数(如图所示)或一个通用的+方法。

word <- function(x, ...) [email protected] 

如果要更新插槽,则会写入替换函数或方法。该函数或方法通常有三个参数,即要更新的对象,可能的附加参数以及用于更新对象的值。这里有一个通用的方法+实施

setGeneric("word<-", function(x, ..., value) standardGeneric("word<-")) 

setReplaceMethod("word", c("MyClass", "character"), function(x, ..., value) { 
    ## note double dispatch on x=MyClass, value=character 
    [email protected] <- value 
    x 
}) 

一个有点棘手替代实施

setReplaceMethod("word", c("MyClass", "character"), function(x, ..., value) { 
    initialize(x, word=value) 
}) 

它使用initialize通用的,默认的方法作为拷贝构造函数;如果同时更新多个插槽,这可能很有效。

由于该类用户看到,一个人想用“秀”的方法,为此,通用(​​)已经存在

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 

效率

ř有效地工作于载体; S4班也不例外。所以设计是一个类的每个插槽表示跨越多行的列,而不是单行的元素。我们期望插槽'word'通常包含一个长度大于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 
[email protected] <- toupper([email protected]) 

注意我们大写的amer_uc但不amer

> [email protected][99 + 1:10] 
[1] "Adana"  "Adar"  "Adar's"  "Addams"  "Adderley" 
[6] "Adderley's" "Addie"  "Addie's" "Addison" "Adela"  
> [email protected][99 + 1:10] 
[1] "ADANA"  "ADAR"  "ADAR'S"  "ADDAMS"  "ADDERLEY" 
[6] "ADDERLEY'S" "ADDIE"  "ADDIE'S" "ADDISON" "ADELA"  

这真的是 - [R用户期待 - 我创建了一个单独的对象并对其进行了修改;原始对象是未修改的。这是我的一个断言;也许我不知道R用户的期望。我假设R用户并没有真正关注这是一个参考类,但认为它只是另一个R对象,如integer()矢量或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) 

但现在我们已经改变了这两个ameramer_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"  
+0

你可以用一些实际的代码来扩展这个注释:*对于Java背景,你会被诱惑去使用引用类,但是它们具有“引用语义”和远处的动作(改变一个对象改变另一个引用相同的数据),而大多数R用户期望'copy on change'语义。* – 2013-04-21 15:36:49

+0

@ G.Grothendieck我在最后添加了一节。 – 2013-04-21 15:59:57

+0

谢谢。我明白你现在指的是什么。从R用户的角度来看,引用类是作为环境实现的,其作用类似于环境,因此将一个引用类对象分配给另一个引用类只会使第二个指向第一个,而不涉及复制对象中的值(如果它通过列表实现)。 proto软件包也基于环境,并且工作方式相同。 – 2013-04-21 16:39:03