Frage

Ich habe ein paar maßgefertigte Referenzkurse und möchte für einige von ihnen Zwangsmethoden schreiben. Es wäre schön, wenn ein Funktionsaufruf so aussehen würde:

objectCoerce(src=obj, to="list", ...)

wo ... ist der entscheidende Teil, da ich manchmal zusätzliche Dinge für bestimmte Zwang bestehen möchte (siehe do.deep = TRUE/FALSE unter.

Um dies zu tun, muss ich jedoch einen "Transformator" implementieren, der das nimmt to Argument versucht, ein leeres Objekt der Klasse zu instanziieren to Und ruft dann den "regulären" Methodenversand auf? Oder gibt es einen besseren Weg?

Unten finden Sie meine aktuelle Lösung. Es funktioniert, aber ich "verliere" die Option, zum Unterricht zu zwingen character" Da diese Klasse verwendet wird, um Dinge an den regulären Dispatcher und a zu verarbeiten to = "character würde zu unendlicher Rekursion führen. Außerdem ist es viel Overhead.

Bearbeiten 2011-12-02

Na sicher setAs wäre die erste Adresse, die es überprüfen würde. Aber die von arg angegebene Funktion def in setAs Kann nur ein Argument nehmen, und das ist oft zu starr für mich. Zum Beispiel sehe ich nicht, wie ich das einschließen kann do.deep = TRUE/FALSE Wechseln Sie bei Verwendung setAs.

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

Generische Methode

setGeneric(
    name="objectCoerce",
    signature=c("src", "to"),
    def=function(src, to, ...){
        standardGeneric("objectCoerce")       
    }
)

Zwischentransformator

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

Zwangsmethode 'myvirtual' zu 'List'

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)

    }
)

Testlauf

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
War es hilfreich?

Lösung

Verwenden Sie möglicherweise SETAS, um eine Koerce -Methode zu erstellen (obwohl man lieber die eigene Basisklasse zum Schreiben der Methode hätte, anstatt dies für EnvrefClass zu tun)

setAs("envRefClass", "list", function(from) {
    fields <- names(getRefClass(class(from))$fields())
    Map(from$field, fields)
})

und dann

> 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

? Die tiefe Version könnte wie sein

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

Ich habe keine guten Ideen, um diese zu kombinieren, außer dass ich eine PSUDO-Klasse 'Deep_List' und eine Koerce-Methode dafür erstellen kann. Ich habe das Gefühl, dass ich Ihren Beitrag nicht verstehe.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top