【问题标题】:How to get the name of the calling function inside the called routine?如何在被调用例程中获取调用函数的名称?
【发布时间】:2023-03-31 08:56:01
【问题描述】:

是否有像函数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.

如果我在evalq 中使用parent.frame(n)n&gt;1,这不会改变。

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

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

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

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

【问题讨论】:

  • 对一个函数以一种特殊的方式来做这件事有点奇怪。为什么不使用traceback(),只要生成任何错误,它就会起作用?
  • @hadley,我在这篇文章中了解了tracebacksys.call(s)。所以有效:-)。想法是有许多函数调用check 来验证它们的输入。如果check 检测到错误,它会转储父环境,因此我可以看到传递给函数的所有输入,然后停止。问题是我无法识别调用check 并导致错误的函数。我可以使用options(error=traceback),但这会转储整个堆栈调用,这可能会导致一些混乱。感谢 cmets!
  • 如果您需要完整参考(函数名、行号、源文件)follow here

标签: r


【解决方案1】:

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

生成当前调用的函数的名称可以通过

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

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

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

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

【讨论】:

  • 或者只是deparse(sys.call())
  • @hadley,不,得到 current 调用。我想要上一级的电话。
  • @hadley 很奇怪,但 sys.call(-1) 适用于 readRDS 但对于 write_csv 它返回 standardise_path(path, input = FALSE)。为什么??
【解决方案2】:

?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

【讨论】:

  • 其实我想让check 捕获test 的名字,而不是反过来。但是按照您的链接,我想通了:可以使用deparse(sys.calls()[[sys.nframe()-1]]) 完成。
  • @Ferdinand.kraft 啊,对不起,我错过了那个细节。不过很高兴它有帮助。您当然可以在test 级别捕获呼叫并将其传递,但​​sys.call() 更简洁。
  • @GavinSimpson 我很想知道您将如何使用 sys.call() 完成此任务。抱歉,我在下面没有读到足够的内容。
  • @GavinSimpson 该方法在命名空间中调用时会失败(例如,在package::check(1) 中)。在我的 R 版本中,这将返回一个字符向量 c("::", "package", "check")。首选电话是deparse(match.call()[[1]])
【解决方案3】:

作为记录,正如 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

【讨论】:

    【解决方案4】:

    问题 #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.")
    

    【讨论】:

    • 对不起,我的第二个问题不是很清楚。请参阅上面的编辑。我认为evalq(stop("invalid input."), parent.frame()) 会给出与父函数在其主体中有stop("invalid input.") 语句相同的结果。
    【解决方案5】:

    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 package (v0.2.1) 于 2019 年 3 月 3 日创建

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-09
        • 1970-01-01
        • 1970-01-01
        • 2021-03-11
        • 1970-01-01
        • 1970-01-01
        • 2010-11-21
        • 1970-01-01
        相关资源
        最近更新 更多