如果我需要根据它们的类以不同的方式处理 R 对象,我可以在单个函数中使用if
和else
:
foo <- function (x) {
if (inherits(x, 'list')) {
# Foo the list
} else if (inherits(x, 'numeric')) {
# Foo the numeric
} else {
# Throw an error
}
}
Run Code Online (Sandbox Code Playgroud)
或者我可以定义一个方法:
foo <- function (x) UseMethod('foo')
foo.list <- function (x) {
# Foo the list
}
foo.numeric <- function (x) {
# Foo the numeric
}
Run Code Online (Sandbox Code Playgroud)
每种方法的优点是什么?有性能影响吗?
好的,有一些背景需要回答这个问题(在我看来)......
在 R 中,当您拥有用户定义的对象结构或对象(例如因子向量或数据框)时,对象的类别是明确的,其中其他属性在对象本身的处理中起着重要作用 - 例如,级别因子向量的标签或数据框中的变量名称是可修改的属性,在访问每个对象的观察中起主要作用。
但是请注意,基本的 R 对象(例如向量、矩阵和数组)是隐式分类的,这意味着该类不使用属性函数标识。无论是隐式还是显式,始终可以使用特定于属性的函数类来检索给定对象的类。
当泛型函数foo
应用于object
具有类属性 c("first", "second") 时,系统会搜索名为 foo.first 的函数,如果找到,则将其应用于对象。如果没有找到这样的函数,foo.second
就会尝试调用一个函数。如果没有类名产生合适的函数,foo.default
则使用该函数(如果存在)。如果没有类属性,则尝试隐式类,然后是default
方法。
函数 class 打印对象继承的类的名称向量。
class
<-设置对象继承的类。
inherits()指示其第一个参数是否继承自what参数中指定的任何类。方法分派基于泛型函数的第一个参数的类进行。如果 which 为 TRUE,则为与返回的长度相同的整数向量。每个元素表示与what的元素匹配的class(x)中的位置;零表示不匹配。如果 which 为 FALSE,那么如果任何名称与任何类匹配,则继承返回 TRUE。
除了inherits() 之外的所有函数都是原始函数。
好的,现在让我们以相反的顺序考虑您的示例......
foo <- function (x) UseMethod('foo')
foo.list <- function (x) {
# Foo the list
}
foo.numeric <- function (x) {
# Foo the numeric
}
Run Code Online (Sandbox Code Playgroud)
现在如果我们使用函数 methods()
methods(foo)
[1] foo.list foo.numeric
see '?methods' for accessing help and source code
> getS3method('foo','list')
function (x) {
# Foo the list
}
Run Code Online (Sandbox Code Playgroud)
因此我们有一个类foo
和两个关联的方法foo.list
和foo.numeric
。因此,我们现在知道 classfoo
具有支持list
和numeric
操作的方法。
好的,现在让我们考虑你的第一个例子......
function (x) {
if (inherits(x, 'list')) {
# Foo the list
print(paste0("List: ", x))
} else if (inherits(x, 'numeric')) {
# Foo the numeric
print(paste0("Numeric: ", x))
} else {
# Throw an error
print(paste0("Unhandled - Sorry!"))
}
}
Run Code Online (Sandbox Code Playgroud)
问题是这不是一个 s3 类,它是一个 R 函数。如果您运行methods()
对foo
返回“没有方法找到了”
> methods(foo)
no methods found
> getS3method('foo','list')
Error in getS3method("foo", "list") : no function 'foo' could be found
Run Code Online (Sandbox Code Playgroud)
那么在第二个例子中发生了什么?inherits() 操作匹配参数的类。inherits() ->方法分派基于泛型函数的第一个参数的类进行。
因此,您的第一个示例只是查找函数参数 x 的类,没有创建或存在 S3 类。
好吧,我在这里有偏见,但对象的类是在 R 中描述实体的最有用的属性之一。您创建的每个对象都被标识,无论是隐式还是显式,都至少具有一个类。R 是一种面向对象的编程语言,这意味着实体被存储为对象并具有作用于它们的方法。
所以第二种方法是我认为的方法。为什么?因为您确实按照预期使用了语言结构。第一种使用 inherits() 的方法明显感觉像是一种黑客攻击。从我个人的角度来看,可读性是理解的关键,因此我担心阅读第一个示例的人可能会问“为什么他们(程序员)采用上述方法,我错过了什么?”。我担心的是,应该避免复杂性,因为它会妨碍代码理解。因此,保持简单有利于代码理解。
在代码性能方面,if-else 解析器通常会比对象查找模型快,尽管查找模型不等同于类映射过程,所以我觉得在这种情况下很难回答性能问题。为什么?这两种方法是不同的。
我希望以上内容为您指明了正确的方向。保持安全,良好的业力随你而去。
这里有几本书推荐: