50

是否有像函数一样获取调用者姓名的“非内部”方式stop

这个想法是我有一个小函数来检查输入并在不满足某些条件时停止执行。此函数由其他几个使用相同验证代码的人调用。如果输入无效,调用者的环境被转储(所以我可以看到传递给函数的参数),并且执行停止。

简化示例:

check <- function(x)
{
    if(x<0)
    {
        print(as.list(parent.frame()))

        evalq(stop("invalid input."), parent.frame())
    }
}

test <- function(x, y)
{
    check(x)
}

我认为评估quote(stop("blah"))调用者环境中的表达式会使其显示调用者的姓名。但是,结果如下:

test(-1, 2)

# $x
# [1] -1
# 
# $y
# [1] 2
# 
# Error in eval(substitute(expr), envir, enclos) : invalid input.

parent.frame(n)如果我使用with n>1in ,这不会改变evalq

所以这是一个问题,实际上是两个问题: 1. 有没有办法获取创建环境的函数的名称(假设它是这样创建的)?2. 为什么上面的解决方法失败了?

编辑:我说上面的解决方法失败了,因为我希望错误消息显示为

Error in test(x, y) : invalid input.

好像stop语句是test身体的一部分。所以问题2可以重述为: 2':stop("invalid input.")考虑到在调用者的环境中评估它,为什么没有捕获调用者的名字?

4

6 回答 6

35

感谢@GavinSimpson 和@RicardoSporta,但我已经弄清楚了。如果有人在 SO 中搜索此内容,我将发布答案。

生成当前调用的函数的名称可以通过以下方式检索

deparse(sys.calls()[[sys.nframe()-1]])

这将返回一个字符串,其中不仅包含函数的名称,还包含整个调用对象。单独的名称可以sys.calls()[[sys.nframe()-1]]在 deparsing 之前通过子集来检索。

我想要这个是因为我编写了一个函数来检查参数并在出现错误时停止执行。但我希望这个函数(i)转储环境并(ii)在执行堆栈中显示上一层的函数名称。(i) 很容易,但我被困在 (ii) 中。

至于我帖子中的第二个问题,情况是这样的:表达式stop("invalid input")是在函数的环境中评估的test,但这与表达式是test主体的一部分不同,因为执行堆栈在这2个场景。在后一种情况下,stop只有test在它上面,但在第一种情况下,它有evalcheck然后test向上。由返回的执行堆栈sys.calls()与封闭环境不同。这可能会引起混淆。

于 2013-03-25T17:49:24.507 回答
23

?match.call。例如:

foo <- function() {
  match.call()[[1]]
}

foo()

as.character(foo())

产生

> foo()
foo
> 
> as.character(foo())
[1] "foo"

您的代码的简化版本是

check <- function(x) {
  match.call()[[1]]
}

test <- function(y) {
  check(y)
}

给予

> test(2)
check
> as.character(test(2))
[1] "check"

Notematch.call()通过使用sys.call()(实际上它调用sys.call(sys.parent())) 来工作,就像我上面所做的那样,没有参数。所以你也不妨咨询一下?sys.call

于 2013-03-24T05:38:43.977 回答
10

作为记录,正如 Hadley 所建议的,您可以使用sys.call(). 例如:

funx = function(...) {
    callingFun = as.list(sys.call(-1))[[1]]
    calledFun = as.list(sys.call())[[1]]
    message(paste(callingFun, " is calling ", calledFun, sep=""))
}

funy = function(...) {funx(...)}

> funy(a = 1, b = 2)
funy is calling funx
于 2014-09-01T12:02:18.760 回答
3

问题 #1 由 Gavin 回答(使用match.call)。

但是,根据您所描述的内容,您还应该查看traceback(),您可以将其输出传递给其他函数。


至于问题 #2

它没有失败,而是完全按预期工作。您看到的错误不是真正意义上的错误,而是您的stop(.)函数的错误。

如果你看一下print(evalq),你会看到它反过来调用eval(substitute(expr), envir, enclos))where expris yourstop("invalid input.")

正确的解决方法是使用更多级别的引用

  evalq(quote(stop("invalid input.")))
  # stop("invalid input.")
于 2013-03-24T05:47:01.527 回答
3

rlang包装的替代方式:

f <- function() {
  name <- rlang::call_frame(n = 2)$fn_name
  rlang::abort(paste("The function", name, "was called and I throw an error."))
}
g <- function() f()
g()
#> Error: The function g was called and I throw an error.

reprex 包(v0.2.1)于 2019 年 3 月 3 日创建

于 2019-03-03T14:01:48.473 回答
0

要获取上述函数的函数名,您可以简单地使用:

gsub(pattern="^([A-Za-z0-9]+)(\\({1})(.*)(\\){1})$",replacement="\\1",x=deparse(sys.call(-1)))
于 2014-04-08T02:46:19.853 回答