Pregunta
Escribí una clase s4 y anulé el operador "==" con
setMethod("==",
signature=c(e1="CAttribute", e2="CAttribute"),
definition=function(e1, e2)
{
return(getName(e1) == getName(e2))
}
)
Si ahora quiero probar si una instancia de CAttribute está en una lista de CAttributes,
a1 <- new("CAttribute", name="a1")
l <- list(new("CAttribute", name="a1"), new("CAttribute", name="a2"))
a1 %in% l
Obtuve el siguiente error
Error in match(x, table, nomatch = 0L) :
'match' requires vector arguments
¿Qué hago mal? ¿Cómo puedo probar una lista de objetos s4 para detectar la aparición de un objeto específico respectivo a un determinado operador "=="?
Solución
Si no anulas %in%
, utilizará la implementación actual de %in%
que puedes encontrar en el ayuda:
"%in%" <- function(x, table) match(x, table, nomatch = 0) > 0
El match
la función no espera un objeto de clase CAttribute
y esto explica tu error.
Otros consejos
Para ser un poco más explícito, como señala @Pop, la función relevante requiere implementación.En este caso parecería que match
es la función relevante. match
necesita ser genérico, restringiendo el envío a argumentos relevantes
setGeneric("match", signature=c("x", "table"))
y luego implementado
setMethod("match", c(x="CAttribute", table="CAttribute"),
function(x, table, nomatch = NA_integer_, incomparables = NULL)
{
match(getName(x), getName(table), nomatch=nomatch,
incomparables=incomparables)
})
Creo que esto normalmente sería suficiente, funciones que usaban match
(como %in%
) ahora funcionaría.Pero por alguna razón, parece necesario promover %in%
a un genérico
setGeneric("%in%")
y para implementar eso
setMethod("%in%", c("CAttribute", "CAttribute"),
function(x, table)
{
match(x, table, nomatch=0L) > 0L
})
Por si sirve de algo, R tiene 'genéricos de grupo', por lo que en lugar de implementar solo los ==
operador sería apropiado implementar, por ejemplo, ?Compare
setMethod("Compare", c(e1="CAttribute", e2="CAttribute"),
definition=function(e1, e2)
{
callGeneric(getName(e1), getName(e2))
})
lo que dice para todos los operadores de comparación, incluido ==
, usar getName()
para la comparación.
Con una definición de clase básica.
CAttribute <- setClass("CAttribute", representation(name="character"))
getName <- function(x) x@name
Tenemos
CAttribute(name="foo") %in% CAttribute(name=c("foo", "bar"))
## [1] TRUE
CAttribute(name="foo") == CAttribute(name=c("foo", "bar"))
## [1] TRUE FALSE
Note que el clase contiene un vector, lo que permite operaciones vectorizadas que hacen que R tenga un rendimiento razonable;Es probable que definir una clase escalar y crear una lista de ellas, como en la pregunta original, sea muy ineficiente.
library(microbenchmark)
microbenchmark(f0=CAttribute(name=rep("A", 1000)),
f1=replicate(1000, CAttribute(name="A")),
times=5)
## Unit: microseconds
## expr min lq median uq max neval
## f0 298.82 306.435 309.681 311.891 334.687 5
## f1 264214.85 277728.310 286446.876 300839.340 301080.928 5
Si uno estuviera usando una lista de escalares, entonces sería necesario implementar, por ejemplo, "coincidencia, atributo CA, método de lista".