2010-12-14 11 views
6

Gibt es eine Möglichkeit zu definieren, was passiert, wenn das Argument einer Methode fehlt oder NULL? Im folgenden Beispiel möchte ich die gleiche Funktion aufrufen, egal ob ich foo() oder foo(NULL) eingib. Natürlich weiß ich, dass ich eine Methode setMethod("foo","NULL",function(x) foo()) haben kann, aber das ist Code-Replikation und eine mögliche Fehlerquelle.S4 fehlende oder NULL Argumente zu Methoden?

Danke!

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" 

Antwort

11

Fast genau drei Jahren Verspätung an die Partei, aber Sie wirklich setClassUnion wollen:

> 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 schafft eine virtuelle Klasse, die eine übergeordnete Klasse ist (Eltern) zu den Komponentenklassen, so dass beide Kinder von dieser Klasse erben, was bedeutet, dass Sie die gleiche Funktion für jedes Kind ausführen können.

+0

Großartig, danke für die Antwort. Das ist was ich will. –

3

setMethod("foo","NULL",function(x) foo()) Verwendung ist kein Code-Replikation, da Sie Code nicht replizieren, sondern einen Anruf setzen. Ich würde sagen, es ist ein sehr guter Weg, um Ihr Problem zu lösen.

+0

@Joris Ich habe versucht, es funktioniert wie erwartet. – mbq

+0

@mbq: Ich weiß, ich habe die falsche Funktion geändert :-) hinzugefügt einen Platz zu Ihrem Beitrag, damit ich upvote. –

+0

Ok, es repliziert keinen "intelligenten" Code, aber repliziert immer noch dummen Code. Es ist anfällig für die Einführung von Fehlern. Ich habe Methoden mit 10 oder 15 Argumenten. Die Verwendung einer zusätzlichen setMethode für jede mögliche Kombination von NULL/fehlend für einige dieser Argumente ist meiner Meinung nach keine gute Wahl. –

2

Ich denke, der richtige Weg „ANY“ in der Signatur zu verwenden ist:

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" 

Stellen Sie sicher, Sie geben alle anderen Möglichkeiten, die Sie getroffen wollen kümmern, als „ANY“ den ganzen Rest nimmt auch, dass passt nicht zur Signatur einer anderen Methode.

Wenn Sie Argumente haben, die fehlen können, können Sie sie einfach nicht in der Signatur von setMethods angeben und einen Standardwert im generischen festlegen. Dies ist - meiner bescheidenen Ansicht nach - eine bessere Design-Wahl.

Jetzt können Sie mit den NULL-Fällen in Code umgehen, als würden Sie mit den fehlenden Argumenten tun.

Auf einer Nebenbemerkung: Jetzt habe ich NULL als Standardwert hinzugefügt, aber in vielen Fällen gibt es viel sinnvollere Auswahlmöglichkeiten für Standardwerte. Denken Sie daran, dass setMethod die anfängliche Signatur annimmt und dass, wenn y als NULL festgelegt ist, dies nicht durch den Standardwert ersetzt wird.

zB:

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) 

DIRTY HACK:

ich den Ansatz ein wenig umständlich zu finden, aber hier ist ein schmutziger Hack, dass alle fehlenden Parameter auf NULL setzt:

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

Probieren Sie dies, erhalten Sie:

> foo(1) 
[1] "numeric" 

> foo(NULL) 
[1] "NULL" 

> foo() 
[1] "NULL" 

So versenden Sie nie mehr zu den vermissten. Du kannst es einfach vergessen. Aber dies ist nicht der richtige Weg, Dinge zu tun ...

+0

Das ist die Lösung, die ich jetzt verwende. Ich möchte spezifischer sein als jeder andere, aber ich denke, es gibt keinen guten Weg? –

+0

@Florian: Ich habe ein Stück Code hinzugefügt, das Sie spezifischer als "any" machen kann, indem Sie die Argumente so ändern, dass sie alle an die NULL-Methoden übergeben werden, aber das ist definitiv ein schmutziger Hack. Warum nicht alle Standardwerte auf NULL setzen, "ANY" verwenden und die NULL-Fälle in Ihrem Code entsprechend behandeln? Das scheint immer noch der sauberste Weg zu sein. –

Verwandte Themen