【问题标题】:Check for missing argument in parent function检查父函数中是否缺少参数
【发布时间】:2015-01-07 15:27:36
【问题描述】:

问题

我有一个函数f,它计算调用它的环境的摘要。在这个简单的示例中,它只是对找到的所有对象求和。

f <- function(){
   x <- ls(parent.frame())
   sum(sapply(x, get, envir=parent.frame()))
}
g <- function(x = 7, y){
    z <- 3
    f()
}

但是,如果从缺少参数的函数中调用,则会引发错误。

R> g(y = 34)
[1] 44

R> g()
Error in FUN(c("x", "y", "z")[[2L]], ...) : 
  argument "y" is missing, with no default

为了适当地处理它,我需要一种方法来判断,从f 中,如果yg 环境中的其他任意对象是g 的参数,那么如果它不见了。

到目前为止我的尝试

我尝试不同的解决方案

debug(f)
g()

当然missing(y) 不起作用,因为y 不是f 的参数。更改评估 missing 的环境也不起作用,因为我们仍在调用堆栈的同一级别:

Browse[2]> eval(missing(y), parent.frame())
Error in missing(y) : 'missing' can only be used for arguments

Browse[2]> identical(sys.frames(), eval(sys.frames(), parent.frame()))
[1] TRUE

我能做的是确定 y 是否是 g 函数的参数,使用脏黑客攻击

Browse[2]> eval(substitute(missing(a), list(a="x")), parent.frame())
[1] TRUE

Browse[2]> eval(substitute(missing(a), list(a="y")), parent.frame())
[1] TRUE

Browse[2]> eval(substitute(missing(a), list(a="z")), parent.frame())
[1] FALSE

对于 xy 两个参数产生 TRUE,但不是普通变量 z。将它与检查参数是否可以检索的tryCatch 结合使用可以解决问题,但它非常脏:

is.argument <- eval(substitute(missing(a), list(a="y")), parent.frame())
if(is.argument){
    tryCatch({
        get("y", parent.frame())
        FALSE
    }, error = function(e) TRUE)
} else {
    NA
}

此外,我不知道如何为任意参数定义is.argument,而不是上面示例中明确声明的"y"

更新:目的

实际上,f 的目的是在运行时调试g。我可能会打电话

R> debug(g)
R> g()

通过它并使用f 检查对象的状态,或者我可以设置options(error=recover),然后发现自己正在调试g,如果它产生错误。在这两种情况下都应该有一个明确定义的调用堆栈,所以我想我的基本问题是它是否可以在不同的级别上进行查询,以类似于帧堆栈的方式(使用sys.frames() 访问)。我必须承认,这对我来说是深水。

f 视为我自己的ls.str 的调整版本,可以这样使用:

Browse[2]> ls.str()   # Inside g()
x :  num 7
y : <missing>

在对ls.strutils:::print.ls_str 进行了一些挖掘之后,我发现它完成了同样的任务

for (nam in x) {
    cat(nam, ": ")
    o <- tryCatch(get(nam, envir = E, mode = M), error = function(e) e)
    if (inherits(o, "error")) {
        cat(if (length(grep("missing|not found", o$message)))
            "<missing>"
        else o$message, "\n", sep = "")
    } else {
        strO <- function(...) str(o, ...)
        do.call(strO, strargs, quote = is.call(o) || is.symbol(o))
    }
}

除非有适当的方法可以做到这一点,否则我只会做一个类似的 hack。

【问题讨论】:

  • 这不是g()f() 的问题吗? y 前者的参数,如果参数丢失,g() 应该在之前调用f()
  • 在大多数情况下我会同意,但我f的目的是为了方便调试其他可能不是我自己编写的函数。
  • @Backlin 如果是这种情况,那么您将如何从这些函数中调用f
  • 请稍等,因为执行我要求的操作的根本原因显然不清楚,我将用更详细的解释更新问题。
  • 缺失测试是否足够?如果g() 有一个名为y 的正式名称,并且在其主体中分配给一个名为y 的变量,你想发生什么? y 是否应该通过 f() 中的 sapply 循环运行?

标签: r environment


【解决方案1】:

缺失参数的值在与环境关联的配对列表中由称为“空符号”的奇数对象表示。事实证明,至少目前,调用quote(expr=) 也返回了“空符号”。 (See here 讨论空符号。)

函数ls_safe() 使用这两个事实来实现另一种缺失测试。它返回由其pos 参数指定的环境中存在的非缺失变量的字符向量。

ls_safe <- function(pos=1) {
    ## Capture the parent environment's frame as a list
    ll <- as.list(parent.frame(pos))
    ## Check for "missing" variables
    ii <- sapply(ll, function(X) identical(X, quote(expr=)))
    names(ll)[!ii]
}

## Then just use ls_safe() in place of ls()
f <- function(){
    x <- ls_safe(pos=2)
    sum(sapply(x, get, envir=parent.frame()))
}

g <- function(x = 7, y){
    z <- 3
    f()
}

g(99)
## [1] 102
g(99, 1000)
## [1] 1102

【讨论】:

    【解决方案2】:

    我遇到了同样的问题:想要一个检查参数是否丢失的函数。我还首先尝试了基于eval 的想法,即使变量丢失,它也总是给我FALSE

    Josh 在上面提供了一个解决方案,但它太具体了。我想要的是一个简洁的检查器函数,我可以添加到我的函数中,以便它们检查必要变量中的缺失并引发信息错误。这是我的解决方案:

    check_missing = function(var_names, error_msg = "[VAR] was missing! Please supply the input and try again.") {
    
      #parent.frame as list
      pf = as.list(parent.frame())
    
      #check each if missing
      for (name in var_names) {
        #is it there at all?
        if (!name %in% names(pf)) {
          stop(name + " is not even found in the parent.frame! Check the variable names!", call. = F)
        }
    
        #check if missing
        if (are_equal(pf[[name]], quote(expr = ))) {
          stop(str_replace(error_msg, pattern = "\\[VAR\\]", name), call. = F)
        }
      }
    
      #all fine
      return(invisible(NULL))
    }
    

    要测试它,请使用:

    test_func = function(y) {
      check_missing("y")
      print("OK")
    }
    

    测试一下:

    test_func(y = )
    # Error: y was missing! Please supply the input and try again.
    # Called from: check_missing("y")
    test_func(y = "k")
    # [1] "OK"
    

    我不满意的另一件事是该错误包含错误的“调用自”消息。它返回检查器函数本身,但如果它返回父函数会提供更多信息。不知道能不能解决。

    希望这可能对某人有用。

    【讨论】:

    • 不错的功能!它解决的问题与我的不同,因为您需要告诉test_func 您要检查的参数的名称。
    • 您可以遍历所有参数并使用quote(expr = ) 检查身份。我不想这样做,因为我的函数经常允许缺少一些参数。
    猜你喜欢
    • 1970-01-01
    • 2016-05-17
    • 2021-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多