R:S3 方法调度取决于参数
R: S3 Method dispatch depending on arguments
我有一个通用函数 foo
,我想根据给它的参数以三种不同的方式调用它。
foo <- function(...) UseMethod("foo")
#default
foo.default <- function(x, y, ...) {
#does some magic
print("this is the default method")
}
#formula
foo.formula <- function(formula, data = list(), ...) {
print("this is the formula method")
}
#data.frame
foo.data.frame <- function(data, x, y, ...) {
print("this is the data.frame method")
}
在下文中,我将展示我是如何期望方法分派工作的,但输出显示在每次调用下...
mydata <- data.frame(x=c(1,2,3,4),y=c(5,6,7,8))
#ways to call default function
foo(x = mydata$x, y = mydata$y)
#[1] "this is the default method"
#ways to call formula
foo(formula = mydata$x~mydata$y)
#[1] "this is the formula method"
foo(formula = x~y, data = mydata)
#[1] "this is the formula method"
foo(data = mydata, formula = x~y) #ERROR
#[1] "this is the data.frame method"
#ways to call data.frame method
foo(data = mydata, x = x, y = y)
#[1] "this is the data.frame method"
foo(x = x, y = y, data = mydata) #ERROR
#Error in foo(x = x, y = y, data = mydata) : object 'x' not found
据我所知,使用的方法取决于第一个参数的 class。本质上,我希望 的方法分派取决于传递给通用函数 foo
的参数 而 而不是第一个参数 。
我希望调度具有以下优先级:
如果存在公式参数,则使用公式方法(此处数据参数应该是可选的)
然后,如果没有找到公式参数,如果存在数据参数,则使用 data.frame 方法(需要 x 和 y 参数)
else foo
需要 x 和 y 参数,否则它将失败。
注
我想避免按如下方式定义通用函数 foo
foo <- function(formula, data,...) UseMethod("foo")
虽然这会解决我的所有问题(我相信除最后一种情况外的所有问题),但这会导致 devtools::check()
警告,因为某些 S3 函数将不会具有与通用函数相同的参数,并且会不再一致(特别是 foo.default 和 foo.data.frame)。而且我不想包含缺少的参数,因为这些方法对这些参数没有用。
正如 Thomas 所指出的,这不是 S3 类 的标准行为。但是,如果你真的想坚持使用 S3,你可以编写你的函数以便 "mimick" UseMethod
,即使它不会很漂亮并且可能不是你想要做的。尽管如此,这里的想法是基于首先捕获所有参数,然后检查是否存在 "preferred" 参数类型:
先获取一些对象:
a <- 1; class(a) <- "Americano"
b <- 2; class(b) <- "Espresso"
让相关函数捕获所有带点的参数,然后按照您的偏好顺序检查是否存在参数类型:
drink <- function(...){
dots <- list(...)
if(any(sapply(dots, function(cup) class(cup)=="Americano"))){
drink.Americano(...)
} else { # you can add more checks here to get a hierarchy
# try to find appropriate method first if one exists,
# using the first element of the arguments as usual
tryCatch(get(paste0("drink.", class(dots[[1]])))(),
# if no appropriate method is found, try the default method:
error = function(e) drink.default(...))
}
}
drink.Americano <- function(...) print("Hmm, gimme more!")
drink.Espresso <- function(...) print("Tripple, please!")
drink.default <- function(...) print("Any caffeine in there?")
drink(a) # "Americano", dispatch hard-coded.
# [1] "Hmm, gimme more!"
drink(b) # "Espresso", not hard-coded, but correct dispatch anyway
# [1] "Tripple, please!"
drink("sthelse") # Dispatches to default method
# [1] "Any caffeine in there?"
drink(a,b,"c")
# [1] "Hmm, gimme more!"
drink(b,"c", a)
# [1] "Hmm, gimme more!"
我有一个通用函数 foo
,我想根据给它的参数以三种不同的方式调用它。
foo <- function(...) UseMethod("foo")
#default
foo.default <- function(x, y, ...) {
#does some magic
print("this is the default method")
}
#formula
foo.formula <- function(formula, data = list(), ...) {
print("this is the formula method")
}
#data.frame
foo.data.frame <- function(data, x, y, ...) {
print("this is the data.frame method")
}
在下文中,我将展示我是如何期望方法分派工作的,但输出显示在每次调用下...
mydata <- data.frame(x=c(1,2,3,4),y=c(5,6,7,8))
#ways to call default function
foo(x = mydata$x, y = mydata$y)
#[1] "this is the default method"
#ways to call formula
foo(formula = mydata$x~mydata$y)
#[1] "this is the formula method"
foo(formula = x~y, data = mydata)
#[1] "this is the formula method"
foo(data = mydata, formula = x~y) #ERROR
#[1] "this is the data.frame method"
#ways to call data.frame method
foo(data = mydata, x = x, y = y)
#[1] "this is the data.frame method"
foo(x = x, y = y, data = mydata) #ERROR
#Error in foo(x = x, y = y, data = mydata) : object 'x' not found
据我所知,使用的方法取决于第一个参数的 class。本质上,我希望 的方法分派取决于传递给通用函数 foo
的参数 而 而不是第一个参数 。
我希望调度具有以下优先级:
如果存在公式参数,则使用公式方法(此处数据参数应该是可选的)
然后,如果没有找到公式参数,如果存在数据参数,则使用 data.frame 方法(需要 x 和 y 参数)
else foo
需要 x 和 y 参数,否则它将失败。
注
我想避免按如下方式定义通用函数 foo
foo <- function(formula, data,...) UseMethod("foo")
虽然这会解决我的所有问题(我相信除最后一种情况外的所有问题),但这会导致 devtools::check()
警告,因为某些 S3 函数将不会具有与通用函数相同的参数,并且会不再一致(特别是 foo.default 和 foo.data.frame)。而且我不想包含缺少的参数,因为这些方法对这些参数没有用。
正如 Thomas 所指出的,这不是 S3 类 的标准行为。但是,如果你真的想坚持使用 S3,你可以编写你的函数以便 "mimick" UseMethod
,即使它不会很漂亮并且可能不是你想要做的。尽管如此,这里的想法是基于首先捕获所有参数,然后检查是否存在 "preferred" 参数类型:
先获取一些对象:
a <- 1; class(a) <- "Americano"
b <- 2; class(b) <- "Espresso"
让相关函数捕获所有带点的参数,然后按照您的偏好顺序检查是否存在参数类型:
drink <- function(...){
dots <- list(...)
if(any(sapply(dots, function(cup) class(cup)=="Americano"))){
drink.Americano(...)
} else { # you can add more checks here to get a hierarchy
# try to find appropriate method first if one exists,
# using the first element of the arguments as usual
tryCatch(get(paste0("drink.", class(dots[[1]])))(),
# if no appropriate method is found, try the default method:
error = function(e) drink.default(...))
}
}
drink.Americano <- function(...) print("Hmm, gimme more!")
drink.Espresso <- function(...) print("Tripple, please!")
drink.default <- function(...) print("Any caffeine in there?")
drink(a) # "Americano", dispatch hard-coded.
# [1] "Hmm, gimme more!"
drink(b) # "Espresso", not hard-coded, but correct dispatch anyway
# [1] "Tripple, please!"
drink("sthelse") # Dispatches to default method
# [1] "Any caffeine in there?"
drink(a,b,"c")
# [1] "Hmm, gimme more!"
drink(b,"c", a)
# [1] "Hmm, gimme more!"