为什么,对于整数向量x,为(x,"numeric")触发加载强制的附加S4方法?

Jos*_*ien 41 r s4

虽然我的问题与最近的问题有关,但我怀疑它的答案与R S4对象系统的详细工作方式有关.

我期待的是:

(TLDR; - 所有指示都as(4L, "numeric")应该发送到一个函数,它的身体as.numeric(4L)用来将它转换为一个"numeric"向量.)

每当as(object, Class)用户将对象转换为所需对象时Class,就会触发对幕后调用coerce().coerce()反过来,有一堆方法根据函数调用的签名调度 - 这里是第一个和第二个参数的类.要查看所有可用S4 coerce()方法的列表,可以运行showMethods("coerce").

这样做表明只有一种方法可以转换为类"numeric".这是签名的from="ANY", to="numeric":

showMethods("coerce")
# Function: coerce (package methods)
# from="ANY", to="array"
#      ... snip ... 
# from="ANY", to="numeric"
#      ... snip ...
Run Code Online (Sandbox Code Playgroud)

该方法用于as.numeric()执行其转换:

getMethod("coerce", c("ANY", "numeric"))
# Method Definition:
# 
# function (from, to, strict = TRUE) 
# {
#     value <- as.numeric(from)
#     if (strict) 
#         attributes(value) <- NULL
#     value
# }
# <environment: namespace:methods>
# 
# Signatures:
#         from  to       
# target  "ANY" "numeric"
# defined "ANY" "numeric"
Run Code Online (Sandbox Code Playgroud)

鉴于它的签名,以及它是coerce()转换为类的唯一方法这一事实"numeric",我希望上面显示的函数是通过调用调度的函数as(4L, "numeric").只有通过运行以下两项检查才能加强这种期望.

## (1) There isn't (apparently!) any specific method for "integer"-->"numeric"
##     conversion
getMethod("coerce", c("integer", "numeric"))
# Error in getMethod("coerce", c("integer", "numeric")) : 
#   no method found for function 'coerce' and signature integer, numeric

## (2) This says that the "ANY"-->"numeric" method will be used for "integer"-->"numeric"
##     conversion    
selectMethod("coerce",  signature=c("integer", "numeric"))
# Method Definition:
# 
# function (from, to, strict = TRUE) 
# {
#     value <- as.numeric(from)
#     if (strict) 
#         attributes(value) <- NULL
#     value
# }
# <environment: namespace:methods>
# 
# Signatures:
#         from      to       
# target  "integer" "numeric"
# defined "ANY"     "numeric"
Run Code Online (Sandbox Code Playgroud)

实际发生了什么:

(TLDR;实际上,调用as(4L, "numeric")load和dispatches到一个什么都不做的方法.)

尽管上面提到的所有迹象,as(4L, "numeric")不会调度coerce()带签名的调用方法c("ANY", "numeric").

以下是一些显示方式:

## (1) as.numeric() would do the job, but as(..., "numeric") does not
class(as(4L, "numeric"))
#[1] "integer"
class(as.numeric(4L))
# [1] "numeric"

## (2) Tracing shows that the "generic" method isn't called
trace("coerce", signature=c("ANY", "numeric"))

as(c(FALSE, TRUE), "numeric")        ## <-- It's called for "logical" vectors
# Tracing asMethod(object) on entry   
# [1] 0 1

as(c("1", "2"), "numeric")           ## <-- and for "character" vectors
# Tracing asMethod(object) on entry   
# [1] 1 2    

as(c(1L, 2L), "numeric")             ## <-- but not for "integer" vectors 
# [1] 1 2

untrace("coerce")
Run Code Online (Sandbox Code Playgroud)

什么方法,那么,使用?好吧,显然调用的行为在as(4L, "numeric") 方法列表中添加了一个新的S4方法coerce(),并且它是使用的方法.
(比较下面的调用的结果会产生什么之前,我们曾试图我们第一次"integer""character"转换.)

## At least one conversion needs to be attempted before the  
## "integer"-->"numeric" method appears.
as(4L, "numeric")  

## (1) Now the methods table shows a new "integer"-->"numeric" specific method   
showMethods("coerce")    
# Function: coerce (package methods)
# from="ANY", to="array"
#      ... snip ... 
# from="ANY", to="numeric"
#      ... snip ...
# from="integer", to="numeric"        ## <-- Here's the new method
#      ... snip ...

## (2) selectMethod now tells a different story
selectMethod("coerce",  signature=c("integer", "numeric"))
# Method Definition:
# 
# function (from, to = "numeric", strict = TRUE) 
# if (strict) {
#     class(from) <- "numeric"
#     from
# } else from
# <environment: namespace:methods>
# 
# Signatures:
#         from      to       
# target  "integer" "numeric"
# defined "integer" "numeric"
Run Code Online (Sandbox Code Playgroud)

我的问题:

  1. 为什么不as(4L, "numeric")派遣到可用的coerce()方法signature=c("ANY", "numeric")

  2. 如何/为什么它会在S4方法表中添加新方法?

  3. 从哪里(在R的源代码或其他地方)确定coerce()方法的定义signature=c("integer", "numeric")

小智 0

查看 的源代码as(),它分为两部分。(为了清晰起见,源代码已被缩短)。首先,它会查找 的现有方法coerce(),如上所述。

function (object, Class, strict = TRUE, ext = possibleExtends(thisClass, 
    Class)) 
{
    thisClass <- .class1(object)
    where <- .classEnv(thisClass, mustFind = FALSE)
    coerceFun <- getGeneric("coerce", where = where)
    coerceMethods <- .getMethodsTable(coerceFun, environment(coerceFun), 
        inherited = TRUE)
    asMethod <- .quickCoerceSelect(thisClass, Class, coerceFun, 
        coerceMethods, where)

    # No matching signatures from the coerce table!!!
    if (is.null(asMethod)) {
        sig <- c(from = thisClass, to = Class)
        asMethod <- selectMethod("coerce", sig, optional = TRUE, 
            useInherited = FALSE, fdef = coerceFun, mlist = getMethodsForDispatch(coerceFun))
Run Code Online (Sandbox Code Playgroud)

如果没有找到任何方法(如本例所示),它将尝试创建一个新方法,如下所示:

        if (is.null(asMethod)) {
            canCache <- TRUE
            inherited <- FALSE

            # The integer vector is numeric!!!
            if (is(object, Class)) {
                ClassDef <- getClassDef(Class, where)
                if (identical(ext, FALSE)) {}
                else if (identical(ext, TRUE)) {}
                else {
                  test <- ext@test

                  # Create S4 coercion method here
                  asMethod <- .makeAsMethod(ext@coerce, ext@simple, 
                    Class, ClassDef, where)
                  canCache <- (!is(test, "function")) || identical(body(test), 
                    TRUE)
                }
            }
            if (is.null(asMethod)) {}
            else if (canCache) 
                asMethod <- .asCoerceMethod(asMethod, thisClass, 
                  ClassDef, FALSE, where)
            if (is.null(asMethod)) {}
            else if (canCache) {
                cacheMethod("coerce", sig, asMethod, fdef = coerceFun, 
                  inherited = inherited)
            }
        }
    }

    # Use newly created method on object here
    if (strict) 
        asMethod(object)
    else asMethod(object, strict = FALSE)
Run Code Online (Sandbox Code Playgroud)

顺便说一句,如果您只处理基本的原子类型,我会坚持使用基本函数并避免使用methods包;使用的唯一原因methods是处理 S4 对象。