【问题标题】:How to write coercion methods如何编写强制方法
【发布时间】:2011-12-01 18:50:01
【问题描述】:

我有一堆定制的引用类,想为其中一些编写强制方法。如果函数调用看起来像这样就好了:

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

其中... 是关键部分,因为有时我想为某些强制传递额外的东西(请参阅下面的do.deep = TRUE/FALSE

但是,为了做到这一点,我是否需要实现一种“转换器”,它采用to 参数,尝试实例化to 指定的类的空对象,然后调用“常规“方法调度?还是有更好的办法?

您将在下面找到我当前的解决方案。它可以工作,但我“失去”了强制转换为character" 类的选项,因为该类用于处理常规调度程序的内容,而to = "character 将导致无限递归。另外,开销很大。

编辑 2011-12-02

当然setAs 将是第一个要检查的地址。但是 setAs 中的 arg def 指定的函数只能接受一个参数,而且这对我来说通常太死板了。例如,我看不出在使用setAs 时如何包含do.deep = TRUE/FALSE 开关。

类定义

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

通用方法

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

中间变压器

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

强制方法 'MyVirtual' 到 '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)

    }
)

试运行

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

【问题讨论】:

    标签: r dispatch coercion s4 reference-class


    【解决方案1】:

    也许使用 setAs 创建一个强制方法(尽管人们宁愿拥有自己的基类来编写该方法,而不是为 envRefClass 这样做)

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

    然后

    > 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
    

    ?深层版本可能像

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

    除了创建一个伪类“deep_list”和一个强制方法之外,我没有将这些组合起来的好主意。我感觉我没看懂你的帖子。

    【讨论】:

    • 马丁,感谢您的回答。但我想强制 B 类加入列表,而不是 A 类。
    • +1 表示指向Map() 的指针,以前不知道那个。关于envRefClass,您是完全正确的,但这只是一种快速的'n'dirty 方法。现在一切都基于虚拟类MyVirtual
    • 我不喜欢 setAs 的地方,这就是我开始考虑自己的“重新发明轮子”方法的原因,因为 defsetAs 只允许 one 参数。对于我的大多数强制场景来说,这太死板了(正如你所说,将do.deep 开关与setAs 结合起来并不容易)。希望这能更清楚地说明问题。
    猜你喜欢
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-18
    相关资源
    最近更新 更多