问题描述
我正在阅读Advanced R by Hadley Wickham,在13.7.3 Group Generics部分感到困惑。
措词让我有些困惑,“……您无法定义自己的组通用……为您的班级定义单个组通用……”,但我认为本节的意思是,如果我定义组通用Math.MyClass
,则Math
对象将覆盖abs
组通用(sign
,MyClass
等)中的所有功能。
可以通过运行以下命令来确认:
my_class <- structure(.Data = -1,class = "MyClass")
my_class
# [1] -1
# attr(,"class")
# [1] "MyClass"
abs(my_class)
# [1] 1
# attr(,"class")
# [1] "MyClass"
Math.MyClass <- function(x) { x }
abs(my_class)
# [1] -1
# attr(,"class")
# [1] "MyClass"
我知道这跟在special naming scheme generic.class
之后,但是为什么.Data
中abs(my_class)
的值会受到影响?
当我创建变量my_class
时,我设置了参数.Data = -1
,并且-1的类为numeric
,应该没有改变:
class(unclass(my_class))
# [1] "numeric"
my_numeric <- unclass(my_class)
class(my_numeric)
# [1] "numeric"
abs(my_numeric)
# [1] 1
那么为什么abs(my_class)
在定义Math.MyClass
之前和之后都不会输出相同的结果(1)?
如果我将组通用定义为Math.MyClass
,在定义Math.MyClass <- function(x) {NextMethod()}
前后,我确实会收到相同的结果,但是拥有组通用的意义何在?
而且,为什么我在运行以下命令时在定义abs(my_matrix)
之前和之后都得到Math.matrix
相同的答案:
my_matrix <- matrix(data = -1:-10,ncol = 5) + 0.0
class(my_matrix)
# [1] "matrix"
class(my_matrix[1,1])
# [1] "numeric"
my_matrix
# [,1] [,2] [,3] [,4] [,5]
# [1,] -1 -3 -5 -7 -9
# [2,] -2 -4 -6 -8 -10
abs(my_matrix)
# [,] 1 3 5 7 9
# [2,] 2 4 6 8 10
Math.matrix <- function(x) { x }
abs(my_matrix)
# [,] 2 4 6 8 10
当我运行以下命令时:
your_class <- structure(.Data = list(-1),class = "YourClass")
your_class
# [[1]]
# [1] -1
#
# attr(,"class")
# [1] "YourClass"
abs(your_class)
# Error in abs(your_class) : non-numeric argument to mathematical function
class(unclass(your_class))
# [1] "list"
your_list <- list(-1)
class(your_list)
# [1] "list"
abs(your_list)
# Error in abs(your_list) : non-numeric argument to mathematical function
很明显class
的{{1}}确实很重要(无论如何最初是因为),因为.Data
和abs(your_class)
都导致相同的错误。
为了使事情变得更具挑战性,我发现运行abs(your_list)
后,MyClass
个对象的一切都会恢复正常:
rm(Math.MyClass)
有人可以更完整地解释什么是组通用类(为什么组通用类存在/它们完成什么/它们与R对象的父子关系/为什么某些对象中的my_class
# [1] -1
# attr(,"class")
# [1] "MyClass"
abs(my_class)
# [1] -1
# attr(,"class")
# [1] "MyClass"
rm(Math.MyClass)
abs(my_class)
# [1] 1
# attr(,"class")
# [1] "MyClass"
自变量在分组通用已定义,其他不是/等)?
如果您觉得使用Python实例更容易解释,那么我在Python中比在R中具有更多的OOP经验。任何帮助将不胜感激!
解决方法
组泛型允许您更改特定数据类型的一组功能的行为。最好的解释方法是看一些例子。如果您运行methods("Math")
,将会看到哪些类定义了此功能。
对于Math.Date
,您会看到
function (x,...)
stop(gettextf("%s not defined for \"Date\" objects",.Generic),domain = NA)
所有要做的就是告诉您,所有这些函数都未为Date对象定义。例如
abs(as.Date("2020-01-01"))
# Error in Math.Date(as.Date("2020-01-01")) :
# abs not defined for "Date" objects
通过在组级别设置此行为,无需对Math组中所有函数的特殊版本进行编码即可告诉您它们并未为该类定义,因为它们不是“那样”的数字。但是,即使该列表中的trunc()
可能会出现错误,但实际上仍然有效
trunc(as.Date("2020-01-01"))
[1] "2020-01-01"
那是因为定义了trunc.Date
。
function (x,...)
round(x - 0.4999999)
因此,组泛型的特殊之处在于,您可以为常见的math-y函数定义默认的“后备”行为。但是,如果要更改该行为,仍然可以为特定功能提供特定于类的实现。
请注意,Math组中列出的所有那些函数都调用相同的Math.MyClass
。该函数有一个变量可用于了解实际调用了哪个函数。该变量称为.Generic
,并在?UseMethod
帮助页面上进行了讨论。例如
Math.MyClass<- function(x) { paste(.Generic,x) }
abs(my_class)
# [1] "abs -1"
trunc(my_class)
# [1] "trunc -1"
exp(my_class)
# [1] "exp -1"
这希望可以清楚地表明,您不希望在不调度.Generic
的情况下直接在其中进行任何转换。有关确实对某些函数类型进行分派的函数示例,请查看Math.difftime
function (x,...)
{
switch(.Generic,abs =,sign =,floor =,ceiling =,trunc =,round =,signif = {
units <- attr(x,"units")
.difftime(NextMethod(),units)
},stop(gettextf("'%s' not defined for \"difftime\" objects",domain = NA))
}
您可以看到,对于特定的功能子集,它将为“常规”实现调度,否则会引发错误。
因此,当您定义
Math.MyClass <- function(x) { x }
基本上,您告诉R,您将处理该类对象对abs()
的调用。而且,当您在实现中未更改地返回x
时,您基本上只是返回了相同的对象,而没有执行任何操作。当您未定义Math.MyClass
时,R会通过“常规”步骤来确定如何调用该函数。由于您没有提供自定义的“身份”功能,因此它会退回到默认的数字行为。
关于matrix
行为为什么没有改变的原因,是因为my_matrix
的类是隐式确定的。如果确实转储了对象,则会看到
dput(my_matrix)
# structure(
# c(-1,-2,-3,-4,-5,-6,-7,-8,-9,-10),# .Dim = c(2L,5L))
请注意,它不会像您的my_class
对象那样将类存储在对象本身中
dput(my_class)
# structure(-1,class = "MyClass")
对于隐式类,分派发生的方式略有不同,它的分派更像是没有多余的类。基于类的调度检查对象上的class
属性。请注意,这会有所不同
my_matrix2 <- structure(my_matrix,class="matrix")
# abs(my_matrix2)
# [,1] [,2] [,3] [,4] [,5]
# [1,] -1 -3 -5 -7 -9
# [2,] -2 -4 -6 -8 -10
# attr(,"class")
# [1] "matrix"
您可以看到在这种情况下,Math.matrix
被调用并且什么都没有改变。