当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


R setGeneric 创建函数的通用版本


R语言 setGeneric 位于 methods 包(package)。

说明

创建命名函数的通用版本,以便可以为其定义方法。如果应用于非泛型函数,对 setMethod 的调用将自动调用 setGeneric

通常不需要显式调用 setGeneric,但不会造成损害,并且可以明确地表明正在为非泛型函数定义方法。

标准调用的形式如下:

setGeneric(name)

其中 name 指定现有函数,可能位于另一个包中。在此包中创建新的通用函数时,另一种选择是:

setGeneric(name, def)

其中函数定义 def 指定形式参数并成为默认方法。

用法

setGeneric(name, def= , group=list(), valueClass=character(),
           where= , package= , signature= , useAsDefault= ,
           genericFunction= , simpleInheritanceOnly = )

参数

name

通用函数的字符串名称。

def

一个可选的函数对象,定义非泛型版本,成为默认方法。这实际上相当于将 def 分配为函数,然后使用对 setGeneric 的单参数调用。

以下参数是专门的,在创建具有非标准函数的新通用函数时可以选择使用。当非泛型位于另一个包中时,不应使用它们。

group

该函数所属的组通用函数的名称。有关方法选择中组通用函数的详细信息,请参阅Methods_Details;有关现有组的信息,请参阅S4groupGeneric

valueClass

指定一个或多个类名的字符向量。泛型函数返回的值必须具有(或扩展)此类或其中一个类;否则会产生错误。

signature

函数的形式参数中的名称向量,在调用 setMethod 时,将允许在此函数的方法签名中使用。默认情况下,通常情况下,这将是除 ... 之外的所有形式参数。

通用函数的非标准签名可用于排除利用惰性求值的参数;特别是,如果无法评估参数,则它不能成为签名的一部分。

虽然 ... 不能用作通用签名的一部分,但可以将其作为签名的唯一元素。如果方法的签名与所有 ... 参数匹配,则将选择方法。有关详细信息,请参阅主题dotsMethods 的文档。不可能在签名中混合 ... 和其他参数。

认为省略签名中的参数可以提高效率通常是错误的。对于方法选择,方法签名中使用的参数才是重要的,并且只有在第一次使用该类组合调用函数时才会认真对待。

simpleInheritanceOnly

将此参数提供为TRUE,以要求仅通过简单继承来继承所选方法;也就是说,从 contains= 参数中指定的超类到 setClass ,或者通过简单继承到类联合或其他虚拟类。如果泛型函数需要确保获得完整的原始对象,而不是经过转换的对象,那么它们应该需要简单的继承。需要简单继承的函数示例包括 initializeshow ,因为根据定义它必须返回与其参数相同的类的对象,因为它声称给出作为其参数提供的对象的完整说明。

useAsDefault

覆盖通常的默认方法机制。仅在定义非标准通用函数时相关。请参阅“专门的本地仿制药”部分。

其余参数对于正常应用程序来说已过时。

package

与此函数关联的包的名称。应从非通用版本中自动确定。

where

将结果对象存储为副作用的位置。默认情况下,存储在包的命名空间中,是唯一安全的选择。

genericFunction

过时的。

setGeneric 函数的存在是因为它的副作用:保存通用函数以允许稍后指定方法。它返回name

基本使用

调用 setGeneric 函数来初始化通用函数,为该函数定义一些方法做准备。

最简单和最常见的情况是name指定一个现有函数,通常在另一个包中。您现在想要为此函数定义方法。在这种情况下,您应该仅提供 name ,例如:

setGeneric("colSums")

必须存在同名的现有函数(在本例中位于包 "base" 中)。非泛型函数可以与调用位于同一包中,通常是在创建新函数及其方法时的情况。当该函数位于另一个包中时,它必须可以通过名称使用,例如通过该包的 NAMESPACE 文件中的 importFrom() 指令。 "base" 中的函数不需要,它们是隐式导入的。

将在当前包中创建该函数的通用版本。现有函数成为默认方法,新通用函数的包槽被设置为原始函数的位置(示例中的"base")。

应注意两种特殊类型的非泛型。通过调用 UseMethod 调度 S3 方法的函数是普通函数,而不是 "genericFunction" 类中的对象。它们与任何其他函数一样都是通用的,但需要一些特殊的注意事项来确保 S4 和 S3 方法分派一致(请参阅 Methods_for_S3 )。

原始函数在 C 代码中处理,并不作为普通函数存在。允许以简单形式调用setGeneric,但不会创建实际的通用函数对象。方法调度将在 C 代码中进行。有关更多详细信息,请参阅有关原语函数的部分。

一个重要的特性是在使用相同函数的每个包中创建相同的通用函数定义setGeneric()称呼。当这些包中的任何一个被加载到R在会话期间,此函数将被添加到通用函数表中,并将包含该函数的所有可用方法的方法表。

在调用 setMethod() 之前调用 setGeneric() 并不是绝对必要的。如果对 setMethod 的调用中指定的函数不是通用函数,则 setMethod 将执行对 setGeneric 本身的调用。如果非泛型位于另一个包中、不分派 S3 方法且不是原语,则会打印一条消息,指出第一次调用 setMethod 时创建了泛型函数。

setGeneric() 的第二个常见用途是创建一个与任何现有函数无关的新通用函数。请参阅下面的asRObject() 示例。这种情况可以像前面的例子一样处理,唯一的区别是非泛型函数存在于当前包中。同样,非通用版本成为默认方法。为了清楚起见,最好将赋值紧接在源代码中对 setGeneric() 的调用之前。

通过提供默认值作为 def 参数而不是对其进行赋值,可以获得完全相同的结果。在某些应用中,不会有完全通用的默认方法。虽然有一个特殊的机制(请参阅“专用本地泛型”部分),但建议提供一个发出错误信号的默认方法,但带有一条消息,尽可能清楚地解释为什么非默认方法是需要。

专门的局部仿制药

setGeneric() 的绝大多数调用应该或者有一个参数以确保现有函数可以具有方法,或者有参数 namedef 以创建一个新的泛型函数和一个可选的默认方法。

可以创建具有非标准签名的泛型函数,或者除了方法分派之外进行额外计算的函数,或者属于一组泛型函数的函数。

这些机制都不应该与来自不同包的非泛型函数一起使用,因为结果是创建一个包与另一个包之间可能不一致的泛型函数。使用任何此类选项时,将为新的通用函数分配一个设置为当前包的包槽,而不是在其中找到该函数的非通用版本的包槽。

有一种机制可以定义非泛型函数的专用泛型版本,即 implicitGeneric 结构。这定义了泛型版本,但随后将函数恢复为非泛型形式,将隐式泛型保存在表中,以便在定义方法时激活。但是,该机制只能合法地用于同一包中的非泛型或由 "methods" 包本身使用。在第一种情况下,没有令人信服的理由不简单地将函数设为通用,并将非通用作为默认方法。有关详细信息,请参阅implicitGeneric

通用函数的主体通常除了通过调用 standardGeneric 来分派方法之外不执行任何操作。在某些情况下,您可能只想在通用函数本身中进行一些额外的计算。只要你的函数最终调用 standardGeneric 就是允许的。请参阅下面的示例"authorNames"

在这种情况下,def 参数将定义非标准泛型,而不是默认方法。应预先分配具有相同名称和调用顺序的现有非通用名称。像往常一样,它将成为默认方法。 (另一种选择是 useAsDefault 参数。)

默认情况下,泛型函数可以返回任何对象。如果提供valueClass,它应该是类名的向量;然后,方法返回的值需要满足指定类之一的is(object, Class)。类的空(即零长度)向量意味着任何内容都是允许的。请注意,可以通过定义非标准通用函数来明确指定对结果的更复杂的要求。

如果 def 参数调用 standardGeneric()(有或没有额外计算)并且不存在该函数的现有非泛型版本,则将创建没有默认方法的泛型。这通常不是一个好主意:最好有一个默认方法来发出错误信号,并发出一条消息来解释为什么未定义默认情况。

通过包含 group 参数,可以创建属于现有组的新通用函数。新泛型的参数列表必须与该组的参数列表一致。请参阅setGroupGeneric 来定义新的组泛型。有关组泛型在调度方法中的作用,请参阅GroupGenericFunctions 和第二个参考文献的 10.5 节。

泛型函数和原始函数

一些基本的R函数被专门实现为原始函数,直接在底层 C 代码中求值,而不是通过求值R语言定义。大多数都有隐式泛型(参见implicitGeneric),并且一旦在其上定义了方法(包括组方法)就成为通用的。其他的不能通用。

为基础包中的原语函数调用 setGeneric() 的不同之处在于,它实际上不会生成显式通用函数。原语方法是从内部 C 代码中选择和分派的,以满足对效率的关注。对于一些内部调度的非原始函数也是如此。其中包括 unlistas.vector

请注意,该实现将原始函数的方法限制为签名,其中签名中的至少一个类是正式的 S4 类。否则内部 C 代码将不会寻找方法。原则上这是一个理想的限制,因为不应允许可选包更改现有数据类型上的基本 R 计算的行为。

要查看原语函数的通用版本,请使用 getGeneric(name) 。函数isGeneric将告诉您当前会话中是否为该函数定义了方法。

请注意,S4 方法只能在“internal generic”和%*% 的原语上设置。

例子


## Specify that this package will define methods for plot()
setGeneric("plot")

## create a new generic function, with a default method
setGeneric("props", function(object) attributes(object))

###   A non-standard generic function.  It insists that the methods
###   return a non-empty character vector (a stronger requirement than
###    valueClass = "character" in the call to setGeneric)

setGeneric("authorNames",
    function(text) {
      value <- standardGeneric("authorNames")
      if(!(is(value, "character") && any(nchar(value)>0)))
        stop("authorNames methods must return non-empty strings")
      value
      })

## the asRObject generic function, from package XR
## Its default method just returns object
## See the reference, Chapter 12 for methods

setGeneric("asRObject", function(object, evaluator) {
        object
})




参考

Chambers, John M. (2016) Extending R, Chapman & Hall. (Chapters 9 and 10.)

Chambers, John M. (2008) Software for Data Analysis: Programming with R Springer. (Section 10.5 for some details.)

也可以看看

Methods_Details 以及用于一般讨论的链接,dotsMethods 用于在 ... 上调度的方法,setMethod 用于方法定义。

相关用法


注:本文由纯净天空筛选整理自R-devel大神的英文原创作品 Create a Generic Version of a Function。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。