Comment écrire des méthodes de coercition
-
27-10-2019 - |
Question
Je vais avoir un tas de classes sur mesure de référence et souhaite écrire des méthodes de coercition pour certains d'entre eux. Ce serait bien si un appel de fonction ressemblerait à ceci:
objectCoerce(src=obj, to="list", ...)
où ...
est la partie cruciale que parfois je veux passer des choses supplémentaires pour certains coercitions (voir ci-dessous do.deep = TRUE/FALSE
.
Toutefois, afin de le faire, dois-je mettre en œuvre une sorte de « transformateur » qui prend l'argument to
, essaie d'instancier un objet vide de la classe spécifiée par to
et appelle ensuite l'envoi de la méthode « régulière »? Ou est-il un moyen de mieux?
Ci-dessous vous trouverez ma solution actuelle. Il fonctionne, mais je suis « perdre » la possibilité de forcer à character"
de classe comme cette classe est utilisée pour les choses de processus au répartiteur régulier et un to = "character
entraînerait une récursion infinie. De plus, il est beaucoup de frais généraux.
EDIT 2011-12-02
Bien sûr setAs
serait la première adresse à vérifier. Mais la fonction spécifiée par arg def
en setAs
ne peut prendre un argument, et souvent qui est trop rigide pour moi. Par exemple, je ne vois pas comment je pourrais inclure le commutateur de do.deep = TRUE/FALSE
lors de l'utilisation setAs
.
Classe Defs
setRefClass(Class="MyVirtual")
setRefClass(
Class="A",
contains="MyVirtual",
fields=list(
x="character"
)
)
setRefClass(
Class="B",
contains="MyVirtual",
fields=list(
x.a="A",
x.b="numeric",
x.c="data.frame"
)
)
setGeneric(
name="objectCoerce",
signature=c("src", "to"),
def=function(src, to, ...){
standardGeneric("objectCoerce")
}
)
Procédé générique
setGeneric(
name="objectCoerce",
signature=c("src", "to"),
def=function(src, to, ...){
standardGeneric("objectCoerce")
}
)
Transformateur intermédiaire
setMethod(
f="objectCoerce",
signature=signature(src="ANY", to="character"),
definition=function(src, to, do.deep=FALSE, ...){
# Transform 'to' to a dummy object of class 'to'
to.0 <- to
# For standard R classes
try.res <- try(eval(substitute(
to <- CLASS(),
list(CLASS=as.name(to.0))
)), silent=TRUE)
# For S4 classes
if(inherits(try.res, "try-error")){
try.res <- try(eval(substitute(
to <- new(CLASS),
list(CLASS=to.0)
)), silent=TRUE)
# For my classes. In order to get an 'hollow' object, some of them
# need to be instantiated by 'do.hollow=TRUE'
if(inherits(try.res, "try-error")){
try.res <- try(eval(substitute(
to <- new(CLASS, do.hollow=TRUE),
list(CLASS=to.0)
)), silent=TRUE)
if(inherits(try.res, "try-error")){
stop(try.res)
}
}
}
# Pass transformed 'to' along so the standard method
# dispatcher can kick in.
out <- objectCoerce(src=src, to=to, do.deep=do.deep, ...)
return(out)
}
)
Méthode Coercion 'myvirtual' à 'liste'
setMethod(
f="objectCoerce",
signature=signature(src="MyVirtual", to="list"),
definition=function(src, to, do.deep=FALSE, ...){
fields <- names(getRefClass(class(src))$fields())
out <- lapply(fields, function(x.field){
src$field(x.field)
})
names(out) <- fields
if(do.deep){
out <- lapply(out, function(x){
out <- x
if(inherits(x, "MyVirtual")){
out <- objectCoerce(src=x, to=to, do.deep=do.deep, .ARGS=.ARGS)
}
return(out)
})
}
return(out)
}
)
Test Run
x <- new("B", x.a=new("A", x="hello world!"), x.b=1:5,
x.c=data.frame(a=c(TRUE, TRUE, FALSE)))
> objectCoerce(src=x, to="list")
$x.a
Reference class object of class "A"
Field "x":
[1] "hello world!"
$x.b
[1] 1 2 3 4 5
$x.c
a
1 TRUE
2 TRUE
3 FALSE
> objectCoerce(src=x, to="list", do.deep=TRUE)
$x.a
$x.a$x
[1] "hello world!"
$x.b
[1] 1 2 3 4 5
$x.c
a
1 TRUE
2 TRUE
3 FALSE
La solution
Peut-être utiliser SETA pour créer une méthode coerce (si on préférerait avoir un propre classe de base pour écrire la méthode, plutôt que de faire cela pour envRefClass)
setAs("envRefClass", "list", function(from) {
fields <- names(getRefClass(class(from))$fields())
Map(from$field, fields)
})
et
> as(new("B"), "list")
$x.a
Reference class object of class "A"
Field "x":
character(0)
$x.b
numeric(0)
$x.c
data frame with 0 columns and 0 rows
? La version profonde peut-être comme
setAs("envRefClass", "list", function(from) {
fields <- names(getRefClass(class(from))$fields())
curr <- Map(from$field, fields)
recurr <- sapply(curr, is, "envRefClass")
curr[recurr] <- lapply(curr[recurr], as, "list")
curr
})
Je n'ai pas de bonnes idées pour combiner ceux-ci, autres que de créer un pseudo-classe « deep_list » et une méthode coerce à cela. Je me sens comme je ne comprends pas votre message.