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", ...)

... 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
Était-ce utile?

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top