如何确保参数是按名称而不是按位置调用的?
How to ensure that parameters have been called by name and not by position?
我正在维护一个 package 以单个函数为中心的函数,其中包含一些强制参数以及许多可选参数。
随着我的函数的成熟,可选参数的顺序正在发生变化,因此按顺序调用它们将导致重大更改。
如果后面这些参数是按位置而不是按名称调用的,我想抛出一个warning/error(不确定什么是最好的)。
这是一些具有预期输出的伪代码:
crosstable = function(data, cols=NULL, ..., by=NULL, opt1=FALSE, opt2=FALSE, opt3=FALSE){
warn_if_unnamed(by)
stop_if_unnamed(opt1)
stop_if_unnamed(opt2)
stop_if_unnamed(opt3)
doStuff(data, cols, by, opt1, opt2, opt3)
}
crosstable(mtcars, c(cyl, am), by=vs, opt3=TRUE) #OK
crosstable(mtcars, c(cyl, am), by=vs, TRUE) #error as `opt1` might become `opt56` in the future
crosstable(mtcars, c(cyl, am), vs, opt2=TRUE) #warning (?) as `by` will not move but it would be clearer
我怎样才能做到这一点?
编辑:
感谢@user2554330 和其他一些 SO post (here),我终于让它工作了,尽管如果与管道一起使用它不会工作:
warn_if_unnamed <- function(argname){
.call = sys.call(-1)
f = get(as.character(.call[[1]]), mode="function", sys.frame(-2))
mc = names(as.list(match.call(definition=f, call=.call))) #
sc = names(as.list(.call))
if(argname %in% mc && !argname %in% sc){
warning(argname," is referenced by position, not name")
}
}
myfun = function(x, y=NULL, opt1=FALSE, opt2=FALSE, opt3=FALSE){
warn_if_unnamed("opt1")
warn_if_unnamed("opt2")
warn_if_unnamed("opt3")
invisible()
}
myfun(1, 2)
myfun(1, 2, T, opt2=1)
#> Warning in warn_if_unnamed("opt1"): opt1 is referenced by position, not name
myfun(1, 2, opt1=T, 1, opt3)
#> Warning in warn_if_unnamed("opt2"): opt2 is referenced by position, not name
#> Warning in warn_if_unnamed("opt3"): opt3 is referenced by position, not name
myfun(1, 2, opt2=T, 1, opt3)
#> Warning in warn_if_unnamed("opt1"): opt1 is referenced by position, not name
#> Warning in warn_if_unnamed("opt1"): opt3 is referenced by position, not name
由 reprex package (v2.0.1)
于 2021-10-20 创建
不过我可能会进行一些重构以将警告集中到一个警告中。
PS:最后一行看起来像是 reprex()
中的错误。
您可以使用 sys.call()
函数查看您的函数是如何被调用的,并 match.call()
查看 R 如何将参数与参数匹配。所以代码为
warn_if_unnamed(by)
将是:
if ("by" %in% names(as.list(match.call())) &&
!"by" %in% names(as.list(sys.call())))
warning("'by' should be named")
可以将它放在一个函数中;您需要使用 sys.call()
和 match.call()
的 where
参数来查看函数调用者的参数,而不是 warn_if_unnamed
本身的参数。
func <- function(data, cols = NULL, ...) {
opt_args <- list(...)
if(length(opt_args) > 0 && is.null(names(opt_args))) {
stop("Optional arguments must be named")
}
allowed_args <- c("opt1", "opt2")
if(length(setdiff(names(opt_args), allowed_args)) > 0) {
warning("Additional unknown arguments are ignored")
}
opt_args
}
# ok
func(iris, c("Sepal.Length", "Sepal.Width"))
#> list()
func(iris, c("Sepal.Length", "Sepal.Width"), opt1 = "foo")
#> $opt1
#> [1] "foo"
# warning
func(iris, c("Sepal.Length", "Sepal.Width"), opt3 = "foo")
#> Warning in func(iris, c("Sepal.Length", "Sepal.Width"), opt3 = "foo"):
#> Additional unknown arguments are ignored
#> $opt3
#> [1] "foo"
# error
func(iris, c("Sepal.Length", "Sepal.Width"), "foo")
#> Error in func(iris, c("Sepal.Length", "Sepal.Width"), "foo"): Optional arguments must be named
由 reprex package (v2.0.1)
于 2021-10-20 创建
这可能是一个 warn_if_unnamed
函数:
warn_if_unnamed <- function(argname){
arguments <- as.list(sys.call(which = -1)) # get arguments of sys.call
arg_name <- deparse(substitute(argname))# get variable name
arg_named <- arg_name %in% names(arguments)
if(!arg_named){
warning(arg_name," is referenced by position, not name")
}
}
使用示例:
myfun <- function( arg1,arg2=2,arg3=3 ) {
warn_if_unnamed(arg2)
}
myfun(1,2)
#In warn_if_unnamed(arg2) : arg2 is referenced by position, not name
错误如果未命名可以类似于 error()
而不是 warning()
我正在维护一个 package 以单个函数为中心的函数,其中包含一些强制参数以及许多可选参数。
随着我的函数的成熟,可选参数的顺序正在发生变化,因此按顺序调用它们将导致重大更改。
如果后面这些参数是按位置而不是按名称调用的,我想抛出一个warning/error(不确定什么是最好的)。
这是一些具有预期输出的伪代码:
crosstable = function(data, cols=NULL, ..., by=NULL, opt1=FALSE, opt2=FALSE, opt3=FALSE){
warn_if_unnamed(by)
stop_if_unnamed(opt1)
stop_if_unnamed(opt2)
stop_if_unnamed(opt3)
doStuff(data, cols, by, opt1, opt2, opt3)
}
crosstable(mtcars, c(cyl, am), by=vs, opt3=TRUE) #OK
crosstable(mtcars, c(cyl, am), by=vs, TRUE) #error as `opt1` might become `opt56` in the future
crosstable(mtcars, c(cyl, am), vs, opt2=TRUE) #warning (?) as `by` will not move but it would be clearer
我怎样才能做到这一点?
编辑:
感谢@user2554330 和其他一些 SO post (here),我终于让它工作了,尽管如果与管道一起使用它不会工作:
warn_if_unnamed <- function(argname){
.call = sys.call(-1)
f = get(as.character(.call[[1]]), mode="function", sys.frame(-2))
mc = names(as.list(match.call(definition=f, call=.call))) #
sc = names(as.list(.call))
if(argname %in% mc && !argname %in% sc){
warning(argname," is referenced by position, not name")
}
}
myfun = function(x, y=NULL, opt1=FALSE, opt2=FALSE, opt3=FALSE){
warn_if_unnamed("opt1")
warn_if_unnamed("opt2")
warn_if_unnamed("opt3")
invisible()
}
myfun(1, 2)
myfun(1, 2, T, opt2=1)
#> Warning in warn_if_unnamed("opt1"): opt1 is referenced by position, not name
myfun(1, 2, opt1=T, 1, opt3)
#> Warning in warn_if_unnamed("opt2"): opt2 is referenced by position, not name
#> Warning in warn_if_unnamed("opt3"): opt3 is referenced by position, not name
myfun(1, 2, opt2=T, 1, opt3)
#> Warning in warn_if_unnamed("opt1"): opt1 is referenced by position, not name
#> Warning in warn_if_unnamed("opt1"): opt3 is referenced by position, not name
由 reprex package (v2.0.1)
于 2021-10-20 创建不过我可能会进行一些重构以将警告集中到一个警告中。
PS:最后一行看起来像是 reprex()
中的错误。
您可以使用 sys.call()
函数查看您的函数是如何被调用的,并 match.call()
查看 R 如何将参数与参数匹配。所以代码为
warn_if_unnamed(by)
将是:
if ("by" %in% names(as.list(match.call())) &&
!"by" %in% names(as.list(sys.call())))
warning("'by' should be named")
可以将它放在一个函数中;您需要使用 sys.call()
和 match.call()
的 where
参数来查看函数调用者的参数,而不是 warn_if_unnamed
本身的参数。
func <- function(data, cols = NULL, ...) {
opt_args <- list(...)
if(length(opt_args) > 0 && is.null(names(opt_args))) {
stop("Optional arguments must be named")
}
allowed_args <- c("opt1", "opt2")
if(length(setdiff(names(opt_args), allowed_args)) > 0) {
warning("Additional unknown arguments are ignored")
}
opt_args
}
# ok
func(iris, c("Sepal.Length", "Sepal.Width"))
#> list()
func(iris, c("Sepal.Length", "Sepal.Width"), opt1 = "foo")
#> $opt1
#> [1] "foo"
# warning
func(iris, c("Sepal.Length", "Sepal.Width"), opt3 = "foo")
#> Warning in func(iris, c("Sepal.Length", "Sepal.Width"), opt3 = "foo"):
#> Additional unknown arguments are ignored
#> $opt3
#> [1] "foo"
# error
func(iris, c("Sepal.Length", "Sepal.Width"), "foo")
#> Error in func(iris, c("Sepal.Length", "Sepal.Width"), "foo"): Optional arguments must be named
由 reprex package (v2.0.1)
于 2021-10-20 创建这可能是一个 warn_if_unnamed
函数:
warn_if_unnamed <- function(argname){
arguments <- as.list(sys.call(which = -1)) # get arguments of sys.call
arg_name <- deparse(substitute(argname))# get variable name
arg_named <- arg_name %in% names(arguments)
if(!arg_named){
warning(arg_name," is referenced by position, not name")
}
}
使用示例:
myfun <- function( arg1,arg2=2,arg3=3 ) {
warn_if_unnamed(arg2)
}
myfun(1,2)
#In warn_if_unnamed(arg2) : arg2 is referenced by position, not name
错误如果未命名可以类似于 error()
而不是 warning()