如何获得R中的函数调用的行号?

时间:2019-12-30 22:22:23

标签: r

出于调试目的,我想打印出调用当前函数的地方的行号(和函数名)。如何在R中得到这个?

我已经看到了getting the source file name的解决方案 但是如何获取行号和函数名?]

编辑:我发现了如何以某种形式从traceback()获取此数据,traceback可以将其打印出来,但是我不确定如何从其中解码信息:

f <- function () {
    traceback(x = 3, max.lines = 1)
}

g <- function()
{
    f()
}

x <- g()

source("file.R") # file with this code
# 5: g() at file.R#20
# 4: eval(ei, envir)
# 3: eval(ei, envir)
# 2: withVisible(eval(ei, envir))
# 1: source("file.R")

str(x[[1]])
# chr "g()"
# - attr(*, "srcref")= 'srcref' int [1:8] 20 1 20 8 1 8 20 20
#  ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment:  0x0000000013a31700> 

2 个答案:

答案 0 :(得分:4)

找到了解决方案!从traceback()的代码中得到它:

f <- function ()
{
    x <- .traceback(x = 1)

    srcloc <- if (!is.null(srcref <- attr(x[[1]], "srcref"))) {
        srcfile <- attr(srcref, "srcfile")
        paste0("Called from ", x[[2]], ", at ", basename(srcfile$filename), "#", srcref[1])
    }

    cat(srcloc, "\n")
}

g <- function()
{
    f()
}

g()
# Called from g(), at file.R#15

为此编写了一个不错的包装函数:

# returns a list, unless fmtstring is specified
# level: 1 - caller of the caller of this function; 2 - its parent, 3 - its grand-parent etc.
# fmtstring: return format string: %f (function), %s (source file), %l (line)
# 
# example: str <- caller_info("Called from %f at %s#%l\n")
# !!! it won't work with e.g. cat(caller_info("Called from %f at %s#%l\n"))
# or cat(paste0(caller_info("Called from %f at %s#%l\n"))) !!!
caller_info <- function (fmtstring = NULL, level = 1) # https://stackoverflow.com/q/59537482/684229
{
    x <- .traceback(x = level + 1)

    i <- 1
    repeat { # loop for subexpressions case; find the first one with source reference
        srcref <- getSrcref(x[[i]])
        if (is.null(srcref)) {
            if (i < length(x)) {
                i <- i + 1
                next;
            } else {
                warning("caller_info(): not found\n")
                return (NULL)
            }
        }
        srcloc <- list(fun = getSrcref(x[[i+1]]), file = getSrcFilename(x[[i]]), line = getSrcLocation(x[[i]]))
        break;
    }

    if (is.null(fmtstring))
        return (srcloc)

    fmtstring <- sub("%f", paste0(srcloc$fun, collapse = ""), fmtstring)
    fmtstring <- sub("%s", srcloc$file, fmtstring)
    fmtstring <- sub("%l", srcloc$line, fmtstring)
    fmtstring
}

这是它的用法:

f <- function ()
{
    str <- caller_info("Called from %f at %s#%l\n")
    cat(str)
}

唯一(次要)的限制是,当在诸如cat(caller_info("Called from %f at %s#%l\n"))cat(paste0(caller_info("Called from %f at %s#%l\n")))之类的子表达式中调用时,R混淆地将这些子表达式作为堆栈级别进行计数,从而将其弄乱了。因此最好避免在表达式中使用此包装器。

答案 1 :(得分:0)

没有简单的函数可以满足您的要求,但是出于调试目的,您可以在函数中调用browser(),然后运行where命令以查看当前的调用堆栈。例如,您可能会看到以下内容:

where 1: calls()
where 2 at ~/temp/test.R#6: print(calls())
where 3 at ~/temp/test.R#9: f()
where 4: eval(ei, envir)
where 5: eval(ei, envir)
where 6: withVisible(eval(ei, envir))
where 7: source("~/temp/test.R", echo = TRUE)

这给出了几个呼叫的位置,但不是全部。

如果您确实想要随手可打印的内容(例如C / C ++中的__LINE____FILE__宏),则要困难一些。这将打印当前位置:

cat("This is line ", getSrcLocation(function() {}, "line"),
  " of ", getSrcFilename(function() {}))

并非所有函数都有名称,R函数不知道您使用其命名的名称,但是您可以使用sys.call()查看当前调用。这样就可以打印所有内容:

  cat("This is line ", getSrcLocation(function() {}, "line"),
      " of ", getSrcFilename(function() {}), 
      " called as", deparse(sys.call()), 
      "\n")

可能会打印

This is line  3  of  test.R  called as f() 

sys.call有一个参数可以向上移动堆栈,但是我不知道一种获取行号信息的方法。

您可以使用以下命令获取进行当前调用的函数的开始位置

cat("Called from ", getSrcFilename(sys.function(-1)), " line ", getSrcLocation(sys.function(-1), "line"), 
    " as ", deparse(sys.call()), "\n")

,它将向您显示进行调用的代码,但是行号仅适用于它来自的函数。这是使函数简短的一个好理由!