如何获取被调用例程中的调用函数的名称?

时间:2013-03-24 05:23:22

标签: r

是否有“非内部”方式来获取调用方的名称,正如函数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) n>1使用evalq,则不会改变。

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

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

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

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

5 个答案:

答案 0 :(得分:17)

请参阅?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"

注意match.call()通过使用sys.call()(实际上它调用sys.call(sys.parent()))工作,因为我在上面调用时没有参数。所以您也可以咨询?sys.call

答案 1 :(得分:17)

感谢@GavinSimpson和@RicardoSporta,但我已经明白了。如果有人在SO中搜索,我会发一个答案。

可以通过

检索生成当前调用的函数的名称
deparse(sys.calls()[[sys.nframe()-1]])

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

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

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

答案 2 :(得分:6)

对于记录,正如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

答案 3 :(得分:3)

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

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


至于问题#2

它没有失败,但完全符合预期。您看到的错误不是真正意义上的错误,而是来自stop(.)函数的错误。

如果您查看print(evalq),您会看到它依次调用eval(substitute(expr), envir, enclos)),其中expr是您的stop("invalid input.")

正确的解决方法是再使用一个引用级别

  evalq(quote(stop("invalid input.")))
  # stop("invalid input.")

答案 4 :(得分:0)

要获取上述功能的功能名称,您只需使用:

gsub(pattern="^([A-Za-z0-9]+)(\\({1})(.*)(\\){1})$",replacement="\\1",x=deparse(sys.call(-1)))