2010-12-14 17 views
6

C'è un modo per definire cosa succede quando l'argomento di un metodo è mancante o NULL?S4 argomenti mancanti o NULL ai metodi?

Nell'esempio seguente, mi piacerebbe chiamare la stessa funzione, indipendentemente dal fatto che digiti foo() o foo(NULL). Naturalmente so che posso avere un metodo setMethod("foo","NULL",function(x) foo()) ma che è la replica del codice e una possibile fonte di errori.

Grazie!

setGeneric("foo",function(x) standardGeneric("foo")) 
setMethod("foo","numeric",function(x) "numeric") 
setMethod("foo","NULL",function(x) "NULL") 
setMethod("foo","missing",function(x) "missing") 

R> foo(1) 
[1] "numeric" 
R> foo() 
[1] "missing" 
R> foo(NULL) 
[1] "NULL" 

risposta

11

quasi esattamente tre anni in ritardo alla festa, ma si vuole veramente setClassUnion:

> setClassUnion("missingOrNULL", c("missing", "NULL")) 
> setGeneric("foo",function(x) standardGeneric("foo")) 
> setMethod("foo","numeric",function(x) "numeric") 
> setMethod("foo","missingOrNULL",function(x) "NULL") 

> foo(1) 
[1] "numeric" 
> foo() 
[1] "NULL" 
> foo(NULL) 
[1] "NULL" 

setClassUnion crea una classe virtuale che è una superclasse (genitore) alle classi di componenti, quindi entrambi i figli ereditano da quella classe, il che significa che è possibile inviare la stessa funzione a ogni bambino.

+0

Grande, grazie per la risposta. Questo è quello che voglio. –

3

Utilizzando setMethod("foo","NULL",function(x) foo()) non è una replica di codice, dal momento che non si replicano codice, ma mette una chiamata ad esso. Direi che è un ottimo modo per risolvere il tuo problema.

+0

@Joris Ho provato, funziona come previsto. – mbq

+0

@mbq: lo so, ho cambiato la funzione sbagliata :-) aggiunto uno spazio al tuo post in modo da poter invogliare. –

+0

Ok, non replica codice "intelligente", ma replica ancora codice stupido. È incline a introdurre bug. Ho metodi con 10 o 15 argomenti. Usare un setMethod aggiuntivo per ogni possibile combinazione di NULL/mancante di alcuni di questi argomenti non è una buona scelta secondo me. –

2

Credo che il modo adeguato è quello di utilizzare "ANY" nella firma:

setGeneric("foo",function(x) standardGeneric("foo")) 
setMethod("foo","numeric",function(x) "numeric") 
setMethod("foo","ANY",function(x) "ANY") 

> foo(1) 
[1] "numeric" 

> foo() 
[1] "ANY" 

> foo(NULL) 
[1] "ANY" 

Assicurarsi di specificare ogni altra possibilità che si desidera curato, come "qualsiasi" prende anche tutto il resto che non si adatta alla firma di un altro metodo.

Se si dispone di argomenti che possono essere mancanti, non è possibile specificarli nella firma di setMethods e impostare un valore predefinito nel generico. Questo è, a mio modesto avviso, una scelta di design migliore.

Ora è possibile gestire i codici NULL in-code come si farebbe con gli argomenti mancanti.

Su un sidenote: ora ho aggiunto NULL come valore predefinito, ma in molti casi ci sono scelte molto più sensate per i valori predefiniti. Ricorda solo che setMethod prende la firma iniziale e che quando y è impostato come NULL, questo non viene sostituito dal valore predefinito.

esempio:

setGeneric("bar",function(x,y=2,...) { 
     standardGeneric("bar") 
    }) 

setMethod("bar",c("numeric","ANY"),function(x,y,...) { 
      x + y 
    }) 
setMethod("bar",c("numeric","numeric"),function(x,y,...) { 
      x - y 
    }) 

> bar(1) 
[1] 3 

> bar(1,2) 
[1] -1 

> bar(1,NULL) # this y is not replaced with the default! 
numeric(0) 

HACK DIRTY:

Trovo l'approccio un po 'imbarazzante, ma qui è un hack sporco che imposta tutti i parametri mancanti per NULL:

setGeneric("foo",function(x,y,z) { 
    pars <- names(formals(foo)) 
    for(i in pars){ 
     tryerr <- try(get(i),silent=T) 
     if(is(tryerr,"try-error")){ assign(i,NULL)} 
    } 
    standardGeneric("foo") 
} 

Provando questo, si ottiene:

> foo(1) 
[1] "numeric" 

> foo(NULL) 
[1] "NULL" 

> foo() 
[1] "NULL" 

Quindi non si spedisce mai più al mancante. Puoi semplicemente dimenticartene. Ma questo non è il modo appropriato di fare le cose ...

+0

Questa è la soluzione che sto usando ora. Mi piacerebbe essere più specifico di tutti, ma immagino che non ci sia un buon modo? –

+0

@Florian: Ho aggiunto un pezzo di codice che può farti essere più specifico di "qualsiasi", cambiando gli argomenti in modo che tutti vengano inviati ai metodi NULL, ma questo è decisamente un trucco sporco. Perché non impostare tutti i valori predefiniti su NULL, utilizzare "ANY" e gestire correttamente i casi NULL nel codice? Sembra ancora il modo più pulito per farlo. –

Problemi correlati