为什么对于一个整数向量x,as(x,“numeric”)会触发加载一个额外的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 ... 

该方法使用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" 

鉴于它的签名,以及它是转换为"numeric"类的唯一coerce()方法,所以我期望上面显示的函数是通过调用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" 

究竟发生了什么:

TLDR;事实上,调用as(4L, "numeric")加载和发送到一个什么也不做的方法。

尽pipeas(4L, "numeric") 没有发送到具有签名c("ANY", "numeric")调用的coerce()方法,但是上面提到的所有指示都是如此。

这里有几个方法来表明:

 ## (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") 

那么使用什么方法? 那么显然调用as(4L, "numeric")的行为在coerce()的方法列表中增加了一个新的S4方法,这就是使用的方法。
(将以下调用的结果与我们尝试将第一个"integer" "character"转换为"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" 

我的问题:

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

  2. 如何/为什么将这个方法添加到S4方法表中?

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

我不确定我是否可以详尽回答你的问题,但我会尽力的。

as()函数的帮助指出:

函数'as'把'object'转换成'Class'类的对象。 在这样做的时候,它采用了一种“强制方法”,使用了S4类和方法,但是采用了某种特殊的方式。

[…]

假设“对象”还不是所需的类,那么“as”首先在用于签名'c(from = class(object),to = Class)'的函数'coerce'的方法表中寻找一个方法'' ,同样的方法select将做它的初始查找。

[…]

如果没有find方法,则“as”查找一个。 首先,如果“类”或“类(对象)”是另一类的超类,则类定义将包含构build胁迫方法所需的信息。 在通常情况下,子类包含超类(即,拥有其所有的插槽),该方法通过提取或replaceinheritance的插槽来构造。

如果您查看as()函数的代码(查看它,请在R控制台中键入(不带圆括号!)),如下所示。 首先,它寻找一个asMethod ,如果它找不到任何它试图构造一个,最后它执行它:

 if (strict) asMethod(object) else asMethod(object, strict = FALSE) 

当你复制粘贴as()函数的代码并定义你自己的函数时,我们称之为myas() – 你可以在刚刚提到的if (strict)之上插入一个print(asMethod)来获得用于强制的函数。 在这种情况下,输出是:

 > myas(4L, 'numeric') function (from, to = "numeric", strict = TRUE) if (strict) { class(from) <- "numeric" from } else from <environment: namespace:methods> attr(,"target") An object of class “signature” from to "integer" "numeric" attr(,"defined") An object of class “signature” from to "integer" "numeric" attr(,"generic") [1] "coerce" attr(,"generic")attr(,"package") [1] "methods" attr(,"class") [1] "MethodDefinition" attr(,"class")attr(,"package") [1] "methods" attr(,"source") [1] "function (from, to = \"numeric\", strict = TRUE) " [2] "if (strict) {" [3] " class(from) <- \"numeric\"" [4] " from" [5] "} else from" [1] 4 

所以,正如你所看到的(看attr(,"source") ), as(4L, 'numeric')只是简单地将类的numeric赋给input对象并返回。 因此,以下两个片段是相同的(对于这种情况!):

 > # Snippet 1 > x = 4L > x = as(x, 'numeric') > # Snippet 2 > x = 4L > class(x) <- 'numeric' 

有趣的是,两者都“没有”。 更有意思的是,反过来,

 > x = 4 > class(x) [1] "numeric" > class(x) <- 'integer' > class(x) [1] "integer" 

我不完全确定(因为class方法似乎在C实现) – 但我的猜测是,当分配类numeric ,它首先检查它是否已经是numeric 。 可能是这样的情况integernumeric (虽然不是double ) – 请参阅下面的“历史exception”报价:

 > x = 4L > class(x) [1] "integer" > is.numeric(x) [1] TRUE 

关于as.numeric :这是一个通用的方法,并调用as.double() ,这就是为什么它“工作”(从R帮助as.numeric ):

R的浮点向量有两个名字:“double”和“numeric”(以前有'real'),这是一个历史的反常现象。

“double”是types的名称。 'numeric'是模式的名称,也是隐式类的名称。

关于问题(1) – (3):魔术发生在顶部的四条线上:

 where <- .classEnv(thisClass, mustFind = FALSE) coerceFun <- getGeneric("coerce", where = where) coerceMethods <- .getMethodsTable(coerceFun, environment(coerceFun), inherited = TRUE) asMethod <- .quickCoerceSelect(thisClass, Class, coerceFun, coerceMethods, where) 

我没有时间去挖掘,抱歉。

希望有所帮助。

查看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)) 

如果在这种情况下没有find任何方法,那么它将尝试创build一个新方法,如下所示:

  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) 

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