2010-12-14 12 views
6

¿Hay alguna forma de definir qué sucede cuando falta el argumento de un método o NULL?S4 ¿falta o argumentos NULL a los métodos?

En el siguiente ejemplo, me gustaría llamar a la misma función independientemente de si escribo foo() o foo(NULL). Por supuesto, sé que puedo tener un método setMethod("foo","NULL",function(x) foo()), pero eso es la replicación del código y una posible fuente de errores.

Gracias!

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" 

Respuesta

11

casi exactamente tres años tarde a la fiesta, pero que realmente quiere 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 clase virtual que es una superclase (principal) a las clases de componentes, por lo que ambos hijos heredan de esa clase, lo que significa que puede enviar la misma función contra cada hijo.

+0

Genial, gracias por la respuesta. Esto es lo que quiero. –

3

Usando setMethod("foo","NULL",function(x) foo()) no es una réplica de código, ya que no se replican código, pero pone una llamada a la misma. Diría que es una muy buena manera de resolver su problema.

+0

@Joris Lo he intentado, funciona como se esperaba. – mbq

+0

@mbq: Lo sé, cambié la función incorrecta :-) agregué un espacio a su publicación para poder votar. –

+0

Ok, no replica el código "inteligente", pero aún reproduce el código estúpido. Es propenso a introducir errores. Tengo métodos con 10 o 15 argumentos. Usar un SetMethod adicional para cada posible combinación de NULL/falta de algunos de esos argumentos no es una buena opción en mi opinión. –

2

supongo que la forma adecuada es utilizar "ANY" en la 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" 

Asegúrese de especificar cualquier otra posibilidad que desea cuidado, como "ANY" también lleva todo el resto que no se ajusta a la firma de otro método.

Si tiene argumentos que pueden faltar, simplemente no puede especificarlos en la firma de setMethods y establecer un valor predeterminado en el genérico. Esta es, a mi humilde opinión, una mejor opción de diseño.

setGeneric("foo",function(x,y=NULL,...) { 
     standardGeneric("foo") 
    }) 

setMethod("foo",c("numeric","ANY"),function(x,y,...) { 
      print(y) 
    }) 
setMethod("foo",c("numeric","numeric"),function(x,y,...) { 
      x + y 
    }) 

> foo(1) 
NULL 

> foo(1,3) 
[1] 4 

> foo(1,NULL) 
NULL 

Ahora puede tratar los casos NULL en código como lo haría con los argumentos faltantes.

En una nota al margen: Ahora agregué NULL como valor predeterminado, pero en muchos casos hay opciones mucho más sensatas para los valores predeterminados. Solo recuerde que setMethod toma la firma inicial, y que cuando y se establece como NULL, esto no se reemplaza por el valor predeterminado.

por ejemplo:

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

encuentro el enfoque un poco incómodo, pero aquí es un truco sucio que establece todos los parámetros que faltan a 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") 
} 

Al intentar esto, obtiene:

> foo(1) 
[1] "numeric" 

> foo(NULL) 
[1] "NULL" 

> foo() 
[1] "NULL" 

Así que nunca más se enviará a la faltante. Puedes olvidarte de eso. Pero esto no es la forma adecuada de hacer las cosas ...

+0

Esa es la solución que estoy usando ahora. Me gustaría ser más específico que ninguno, pero supongo que no hay una buena manera. –

+0

@Florian: Agregué un fragmento de código que puede hacer que sea más específico que "cualquiera", cambiando los argumentos para que todos se envíen a los métodos NULL, pero esto definitivamente es un hack sucio. ¿Por qué no establecer todos los valores predeterminados en NULL, usar "ANY" y tratar los casos NULL de manera adecuada en su código? Esa sigue siendo la forma más limpia de hacerlo. –

Cuestiones relacionadas