R中的missing()函数与变量名

4
在R 3.0.2中,missing()函数可以告诉我们一个形式参数是否缺失。
如何避免在missing()函数中硬编码变量名?例如:
demoargs <- function(a=3, b=2, d) {
    f <- formals(demoargs)  # Capture formal arguments
    formalNames <- names(f) # get variable names: a, b, d
    ...   
}

我希望能够检查缺失的表单内容,而不需要以硬编码的方式实现,例如:
missing(formalNames[1])  # returns invalid use of missing!

与缺失(d)不同,目的是为了遍历大量可选择处理方式有限的可选参数。我原本希望使用get或as.name能够指引我正确的方向,但事实并非如此。
另外,我认为可以使用可变参数(...)来完成此操作,但调用者可以通过检查函数声明来查看可接受的可选参数,这将更好。
谢谢, Marie
2个回答

8
你可能首先尝试了类似于missing(as.name(formalNames[1]))missing(formalNames[1])的代码,但发现它们无法正常工作。
原因在于missing()是一种奇怪的函数,类似于library()debug()等函数,可以接受名称或名称的字符表示形式作为参数。这样做“好”在于missing(a)missing("a")都可以检查函数调用是否包含了提供的参数a;但当你执行missing(formalNames[1])时,它会去寻找一个名为formalNames[1]的不存在的参数,这就不太好了。
解决方法是使用do.call(),它在将第二个参数的元素传递给第一个参数指定的函数之前,会先对其进行求值。下面是你可能要执行的代码:
demoargs <- function(a=3, b=2, d) {
    formalNames <- names(formals()) # get variable names: a, b, d
    do.call(missing, list(formalNames[1]))
}

## Try it out
demoargs(a=42)
# [1] FALSE
demoargs()
# [1] TRUE

1
我们可以构建调用 missing("a") 并对其进行评估,有不同的方法来完成这个过程:
demoargs1 <- function(a=3, b=2, d) {
  formalNames <- names(formals())
  eval(bquote(missing(.(formalNames[1]))))   
}
demoargs1(3)
#> [1] FALSE
demoargs1()
#> [1] TRUE

demoargs2 <- function(a=3, b=2, d) {
  formalNames <- names(formals())
  eval(substitute(missing(X), list(X = formalNames[1])))   
}
demoargs2(3)
#> [1] FALSE
demoargs2()
#> [1] TRUE

demoargs3 <- function(a=3, b=2, d) {
  formalNames <- names(formals())
  eval(call("missing", formalNames[1]))   
}
demoargs3(3)
#> [1] FALSE
demoargs3()
#> [1] TRUE

demoargs4 <- function(a=3, b=2, d) {
  formalNames <- names(formals())
  eval(as.call(c(quote(missing), formalNames[1])))
}
demoargs4(3)
#> [1] FALSE
demoargs4()
#> [1] TRUE

另一种方法是检查参数是否在调用中:
demoargs5 <- function(a=3, b=2, d) {
  formalNames <- names(formals())
  formalNames[1] %in% names(match.call()[-1])
}
demoargs5(3)
#> [1] TRUE
demoargs5()
#> [1] FALSE

让我们将Josh的提案加入批处理并进行基准测试:
demoargs_josh <- function(a=3, b=2, d) {
  formalNames <- names(formals())
  do.call(missing, list(formalNames[1]))
}
microbenchmark::microbenchmark(
  demoargs_josh(),
  demoargs1(),
  demoargs2(),
  demoargs3(),
  demoargs4(),
  demoargs5(),times = 100000)
#> Unit: microseconds
#>             expr  min   lq      mean median   uq    max neval   cld
#>  demoargs_josh()  2.5  3.1  5.237294    3.5  4.4 7389.8 1e+05 a    
#>      demoargs1() 11.7 13.4 21.198503   14.0 19.0 5708.1 1e+05     e
#>      demoargs2()  3.1  3.7  6.158610    4.0  5.1 6628.5 1e+05   c  
#>      demoargs3()  2.8  3.3  5.465650    3.7  4.6 2936.4 1e+05 ab   
#>      demoargs4()  2.9  3.5  5.712464    3.8  4.9 2104.8 1e+05  b   
#>      demoargs5()  5.1  6.2 10.056112    6.7  8.7 6204.4 1e+05    d

此内容于2019-10-24由reprex package (v0.3.0)创建

使用do.call()似乎稍微更快,但使用call()as.call()显示出非常相似的性能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接