定义 S4 class 继承自函数
Define S4 class inheriting from function
我正在尝试编写一个 S4 class,专门 returns 一个与输入长度相同的数字向量。我想我很接近;我现在遇到的问题是我只能从我的 GlobalEnv.
中的函数创建新的 classes
library(S4Vectors)
setClass("TransFunc", contains = c("function"), prototype = function(x) x)
TransFunc <- function(x) {
if (missing(x)) return(new("TransFunc"))
new2("TransFunc", x)
}
.TransFunc.validity <- function(object) {
msg <- NULL
if (length(formals(object)) > 1) {
msg <- c(msg, "TransFunc must only have one argument.")
}
res1 <- object(1:5)
res2 <- object(1:6)
if (length(res1) != 5 || length(res2) != 6) {
msg <- c(msg, "TransFunc output length must equal input length.")
}
if (!class(res1) %in% c("numeric", "integer")) {
msg <- c(msg, "TransFunc output must be numeric for numeric inputs.")
}
if (is.null(msg)) return(TRUE)
msg
}
setValidity2(Class = "TransFunc", method = .TransFunc.validity)
mysqrt <- TransFunc(function(x) sqrt(x))
mysqrt <- TransFunc(sqrt) ## Errors... why??
## Error in initialize(value, ...) :
## 'initialize' method returned an object of class “function” instead
## of the required class “TransFunc”
class 直接从函数继承的好处是能够将它们用作常规函数:
mysqrt(1:5)
## [1] 1.000000 1.414214 1.732051 2.000000 2.236068
body(mysqrt) <- expression(sqrt(x)^2)
mysqrt(1:10)
## [1] 1 2 3 4 5 6 7 8 9 10
为什么在全局环境外传递函数会出错?
它不适用于 sqrt
,因为 sqrt 是 primitive
。
我不知道有任何函数只接受一个参数并且不是原始函数。因此,我降低了你的有效性,以演示你的代码如何与预加载包中的其他函数一起工作:
#using your class definition and counstructor
.TransFunc.validity <- function(object) {
msg <- NULL
res1 <- object(1:5)
if (!class(res1) %in% c("numeric", "integer")) {
msg <- c(msg, "TransFunc output must be numeric for numeric inputs.")
}
if (is.null(msg)) return(TRUE)
msg
}
setValidity2(Class = "TransFunc", method = .TransFunc.validity)
这是 mean
默认版本的结果
mymean <- TransFunc(mean.default)
mymean(1:5)
[1] 3
这里有一个变通方法,通过修改 initialize
为您的 class 捕获基元并将它们转换为闭包:
#I modified the class definition to use slots instead of prototype
setClass("TransFunc", contains = c("function"))
TransFunc <- function(x) {
if (missing(x)) return(new("TransFunc"))
new2("TransFunc", x)
}
# Keeping your validity I changed initilalize to:
setMethod("initialize", "TransFunc",
function(.Object, .Data = function(x) x , ...) {
if(typeof(.Data) %in% c("builtin", "special"))
.Object <- callNextMethod(.Object, function(x) return(.Data(x)),...)
else
.Object <- callNextMethod(.Object, .Data, ...)
.Object
})
我得到了以下结果
mysqrt <- TransFunc(sqrt)
mysqrt(1:5)
[1] 1.000000 1.414214 1.732051 2.000000 2.236068
编辑:
在评论中@ekoam 为您的 class:
提出了一个更通用的 initilaize 版本
setMethod("initialize", "TransFunc", function(.Object, ...)
{maybe_transfunc <- callNextMethod();
if (is.primitive(maybe_transfunc))
.Object@.Data <- maybe_transfunc
else .Object <- maybe_transfunc;
.Object})
编辑 2:
@ekoam 给出的方法不维护新的 class。例如:
mysqrt <- TransFunc(sqrt)
mysqrt
# An object of class "TransFunc"
# function (x) .Primitive("sqrt")
mysqrt
# function (x) .Primitive("sqrt")
第一个提议的方法确实有效并维护了新的 class。正如评论中所讨论的,另一种方法是在构造函数期间捕获原语,而不是创建自定义初始化方法:
library(pryr)
TransFunc <- function(x) {
if (missing(x)) return(new("TransFunc"))
if (is.primitive(x)) {
f <- function(y) x(y)
# This line isn't strictly necessary, but the actual call
# will be obscured and printed as 'x(y)' requiring something
# like pryr::unenclose() to understand the behavior.
f <- make_function(formals(f), substitute_q(body(f), environment(f)))
} else {
f <- x
}
new2("TransFunc", f)
}
我正在尝试编写一个 S4 class,专门 returns 一个与输入长度相同的数字向量。我想我很接近;我现在遇到的问题是我只能从我的 GlobalEnv.
中的函数创建新的 classeslibrary(S4Vectors)
setClass("TransFunc", contains = c("function"), prototype = function(x) x)
TransFunc <- function(x) {
if (missing(x)) return(new("TransFunc"))
new2("TransFunc", x)
}
.TransFunc.validity <- function(object) {
msg <- NULL
if (length(formals(object)) > 1) {
msg <- c(msg, "TransFunc must only have one argument.")
}
res1 <- object(1:5)
res2 <- object(1:6)
if (length(res1) != 5 || length(res2) != 6) {
msg <- c(msg, "TransFunc output length must equal input length.")
}
if (!class(res1) %in% c("numeric", "integer")) {
msg <- c(msg, "TransFunc output must be numeric for numeric inputs.")
}
if (is.null(msg)) return(TRUE)
msg
}
setValidity2(Class = "TransFunc", method = .TransFunc.validity)
mysqrt <- TransFunc(function(x) sqrt(x))
mysqrt <- TransFunc(sqrt) ## Errors... why??
## Error in initialize(value, ...) :
## 'initialize' method returned an object of class “function” instead
## of the required class “TransFunc”
class 直接从函数继承的好处是能够将它们用作常规函数:
mysqrt(1:5)
## [1] 1.000000 1.414214 1.732051 2.000000 2.236068
body(mysqrt) <- expression(sqrt(x)^2)
mysqrt(1:10)
## [1] 1 2 3 4 5 6 7 8 9 10
为什么在全局环境外传递函数会出错?
它不适用于 sqrt
,因为 sqrt 是 primitive
。
我不知道有任何函数只接受一个参数并且不是原始函数。因此,我降低了你的有效性,以演示你的代码如何与预加载包中的其他函数一起工作:
#using your class definition and counstructor
.TransFunc.validity <- function(object) {
msg <- NULL
res1 <- object(1:5)
if (!class(res1) %in% c("numeric", "integer")) {
msg <- c(msg, "TransFunc output must be numeric for numeric inputs.")
}
if (is.null(msg)) return(TRUE)
msg
}
setValidity2(Class = "TransFunc", method = .TransFunc.validity)
这是 mean
mymean <- TransFunc(mean.default)
mymean(1:5)
[1] 3
这里有一个变通方法,通过修改 initialize
为您的 class 捕获基元并将它们转换为闭包:
#I modified the class definition to use slots instead of prototype
setClass("TransFunc", contains = c("function"))
TransFunc <- function(x) {
if (missing(x)) return(new("TransFunc"))
new2("TransFunc", x)
}
# Keeping your validity I changed initilalize to:
setMethod("initialize", "TransFunc",
function(.Object, .Data = function(x) x , ...) {
if(typeof(.Data) %in% c("builtin", "special"))
.Object <- callNextMethod(.Object, function(x) return(.Data(x)),...)
else
.Object <- callNextMethod(.Object, .Data, ...)
.Object
})
我得到了以下结果
mysqrt <- TransFunc(sqrt)
mysqrt(1:5)
[1] 1.000000 1.414214 1.732051 2.000000 2.236068
编辑:
在评论中@ekoam 为您的 class:
setMethod("initialize", "TransFunc", function(.Object, ...)
{maybe_transfunc <- callNextMethod();
if (is.primitive(maybe_transfunc))
.Object@.Data <- maybe_transfunc
else .Object <- maybe_transfunc;
.Object})
编辑 2:
@ekoam 给出的方法不维护新的 class。例如:
mysqrt <- TransFunc(sqrt)
mysqrt
# An object of class "TransFunc"
# function (x) .Primitive("sqrt")
mysqrt
# function (x) .Primitive("sqrt")
第一个提议的方法确实有效并维护了新的 class。正如评论中所讨论的,另一种方法是在构造函数期间捕获原语,而不是创建自定义初始化方法:
library(pryr)
TransFunc <- function(x) {
if (missing(x)) return(new("TransFunc"))
if (is.primitive(x)) {
f <- function(y) x(y)
# This line isn't strictly necessary, but the actual call
# will be obscured and printed as 'x(y)' requiring something
# like pryr::unenclose() to understand the behavior.
f <- make_function(formals(f), substitute_q(body(f), environment(f)))
} else {
f <- x
}
new2("TransFunc", f)
}