2013-04-20 18 views
5

Nei linguaggi di programmazione comuni come java ogni file corrisponde normalmente a una classe.Struttura di file e directory di un progetto r

Ho appena iniziato con R. Mi piacerebbe costruire un piccolo programma e ho voluto creare una certa struttura di file e directory come questo

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 

Così mi piacerebbe fare qualcosa di simile (pseudo codice):

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 

quindi questo è piuttosto astratto, ma volevo solo sapere se questo è in linea di principio possibile in R.

UPDATE: Ho bisogno di ottenere più specifico. Quindi la domanda: come tradurrebbe il seguente programma java in un programma R mantenendo lo stesso numero di file e struttura del seguente programma giocattolo?

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

Quali odi * questo * in "è possibile?" fare riferimento a? È certamente possibile inserire diverse parti del programma in file diversi se questa è la domanda. Le classi di riferimento funzionano secondo le linee del tuo pseudo codice se questa è la domanda. Vedi '? SetRefClass'. –

+0

Si noti che uno stile di programmazione orientato agli oggetti non è ciò che è più naturale per R, che ha più di uno stile funzionale di programmazione. –

+0

La programmazione OO è così innaturale per R che è stata implementata circa quattro o cinque volte, mentre lo stile funzionale della programmazione è stato implementato una sola volta, con il linguaggio S stesso, e da allora non è cambiato molto. – Spacedman

risposta

7

classi di riferimento seguito tentano di replicare il codice Java in questione utilizzando R in quanto più vicino modo possibile. A tale riguardo, i tre sistemi di classe R incorporati (S3, S4, Classi di riferimento) sembrano essere i più vicini a tale stile. Reference Classes è il più recente sistema di classe da aggiungere a R e la sua rapida diffusione potrebbe essere dovuta ai programmatori Java che arrivano a R che hanno familiarità con quello stile. file di

(Se si crea un pacchetto da questa poi omettere tutte le dichiarazioni di origine.)

Main.R: file di

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") 
) 

ProcessData File .R:

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

Per eseguire:

source("Main.R") 
main() 

pacchetto proto Il proto package implementa il modello prototipo di programmazione orientata agli oggetti che ha avuto origine con il linguaggio di programmazione Sé ed esiste una certa misura in javascript, Lua ed è particolarmente base io language. proto può facilmente emulare questo stile (come discusso nella sezione Tratti del proto vignette):

lima Main.R: File

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: File

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

ProcessData.R :

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

Per eseguire:

source("Main.R") 
main() 

AGGIORNAMENTO: Aggiunto esempio di prototipo.

AGGIORNAMENTO 2: Migliorato main e MyClass nell'esempio di classe di riferimento.

+0

Grazie! Funziona bene. – toom

10

sistemi di classe

Scopri i tre sistemi di classe in R, S3, S4 e Referen classi

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

## S4 classes 
?Classes 
?Methods 

## Reference classes 
?ReferenceClasses 

Con un background di Java sarete tentati di andare con le classi di riferimento, ma questi hanno 'semantica di riferimento' e azione a distanza (cambiando un oggetto cambia un altro che si riferisce agli stessi dati), mentre la maggior parte Gli utenti R si aspettano una semantica 'copia su cambiamento'. Si possono fare grandi progressi con le classi S3, ma un approccio più disciplinato sarebbe a mio avviso adottare S4. Le caratteristiche di S4 ti sorprenderanno, in parte perché il sistema di classe è più vicino al comune sistema di oggetti Lisp che a Java.

Ci sono other opinions and options.

implementazione di base

Io non sono davvero sicuro di quello che il vostro obiettivo design con `ProcessData' è; Implementerei le tue due classi come una classe, un generico e un metodo per il generico che opera sulla classe 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") 
}) 

Questo è completo e funzionante

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

Le tre linee di codice possono essere collocati in due file, "AllGenerics.R" e "MyClass.R" (compreso il metodo) o tre file "AllGenerics.R", "AllClasses.R", "processData-methods.R" (si noti che i metodi sono associati ai generici e vengono inviati alla classe).

Ulteriori implementazione

Uno normalmente aggiungere un costruttore più user-friendly, per esempio, fornendo suggerimenti per l'utente sui tipi di dati attesi o l'esecuzione di inizializzazione argomento complesso passi

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

Tipicamente si vuole uno slot accessor, piuttosto che l'accesso diretto allo slot. Questa può essere una semplice funzione (come illustrato) o un metodo generico +.

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

Se lo slot deve essere aggiornato, uno scrive una funzione o un metodo di sostituzione. La funzione o il metodo di solito ha tre argomenti, l'oggetto da aggiornare, possibili argomenti aggiuntivi e il valore con cui aggiornare l'oggetto. Ecco un'implementazione metodo generico +

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 
}) 

Un po 'difficile implementazione alternativa è

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

che utilizza il initialize metodo generico e di default come un costruttore di copia; questo può essere efficiente se si aggiornano più slot contemporaneamente.

Poiché la classe viene visto dagli utenti, si vuole visualizzare in modo intuitivo utilizzando un metodo 'show', per il quale un generico (getGeneric("show")) esiste già

setMethod("show", "MyClass", function(object) { 
    cat("class:", class(object), "\n") 
    cat("word:", word(object), "\n") 
}) 

Ed ora il nostro utente sessione assomiglia

> myClass 
class: MyClass 
word: hello world 
> word(myClass) 
[1] "hello world" 
> word(myClass) <- "goodbye world" 
> processData(myClass) 
processData(MyClass) = goodbye world 

efficienza

R funziona in modo efficiente su vettori; Le classi S4 non fanno eccezione. Quindi il design è che ogni slot di una classe rappresenta una colonna che si estende su più righe, piuttosto che l'elemento di una singola riga. Ci aspettiamo che la "parola" dello slot in genere contenga un vettore di lunghezza molto maggiore di 1 e che le operazioni agiscano su tutti gli elementi del vettore. Quindi, si potrebbe scrivere metodi con questo in mente, ad esempio, la modifica del metodo show per

setMethod("show", "MyClass", function(object) { 
    cat("class:", class(object), "\n") 
    cat("word() length:", length(word(object)), "\n") 
}) 

Qui ci sono oggetti più grandi di dati (utilizzando i file sul mio sistema 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 

e tutto questo è abbastanza performante.

pericoli della classe di riferimento 'action-at-a-distance' rewind

Let ad una più semplice implementazione della classe S4, con accesso diretto e slot per non costruttori di fantasia. Ecco il dizionario e una copia, trasformata in maiuscolo

.MyClass <- setClass("MyClass", representation(word="character")) 
amer <- .MyClass(word=readLines("/usr/share/dict/american-english")) 
amer_uc <- amer 
[email protected] <- toupper([email protected]) 

Nota che abbiamo in maiuscolo amer_uc ma non 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"  

Questo è veramente ciò che gli utenti si aspettano R - I ho creato un oggetto separato e lo ho modificato; l'oggetto originale non è stato modificato. Questa è un'affermazione da parte mia; forse non so cosa si aspettano gli utenti R.Suppongo che un utente R non presti realmente attenzione al fatto che questa è una classe di riferimento, ma pensa che sia solo un altro oggetto R come un vettore integer() o un data.frame o il valore restituito di lm().

Al contrario, ecco una minima implementazione di una classe di riferimento, e operazioni simili

.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) 

Ma ora abbiamo cambiato sia amer e amer_uc! Completamente atteso dai programmatori C o Java, ma non dagli utenti 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

Potresti espandere questo commento con un codice reale: * Con uno sfondo Java sarai tentato di seguire le classi di riferimento, ma queste hanno 'semantica di riferimento' e azione a distanza (cambiando un oggetto ne cambia un altro che si riferisce al stessi dati), mentre la maggior parte degli utenti R si aspetta una semantica 'copia su cambiamento'. * –

+0

@ G.Grothendieck Ho aggiunto una sezione alla fine. –

+0

Grazie. Vedo a cosa ti stai riferendo ora. Dal punto di vista di un utente R le classi di riferimento sono implementate come ambienti e si comportano come ambienti in modo che l'assegnazione di un oggetto di classe di riferimento a un altro faccia solo il secondo punto al primo e non implichi la copia dei valori nell'oggetto (come sarebbe il caso se sono stati implementati tramite liste). Il pacchetto proto si basa anche sugli ambienti e funziona allo stesso modo. –