Rで関数呼び出しの行番号を取得する方法は?


8

デバッグのために、現在の関数が呼び出された場所の行番号(および関数名)を出力します。これをRで取得するにはどうすればよいですか?

ソースファイル名取得する解決策を見てきまし たが、行番号と関数名を取得する方法は?]

編集:私はこのデータ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> 

回答:


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"))orのような部分式で呼び出されるとcat(paste0(caller_info("Called from %f at %s#%l\n")))、Rはこれらの部分式を混乱させてスタックレベルとして数え、混乱させます。したがって、式でこのラッパーを使用しないようにしてください。


いいね!のオプションの引数について知りませんでした.traceback()
user2554330

1
私が作ると思います唯一の変化は、抽出機能を使用することでgetSrcrefgetSrcFilenameそしてgetSrcLocation代わりに、属性に直接作業の。内部フォーマットは変更される可能性があります。
user2554330

@ user2554330ありがとう、素晴らしいコメント!:-)ラッパーを更新しました。
TMS 2019

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")

これにより、呼び出しを行ったコードが表示されますが、行番号は、その呼び出し元の関数についてのみです。関数を短く保つための良い議論です!


うーん...これはおもしろいです...しかし、私がこの関数の呼び出しの行番号と関数(それが呼び出された場所)を取得することは本当に重要です。現在の行番号は明確で興味深いものではありません。
TMS 2019

次にbrowser()、エラーまたは警告によってトリガーされる可能性のあるを使用するか、またはを使用してcat("Called from ",getSrcFilename(sys.function(-1)), " line ",getSrcLocation(sys.function(-1), "line"), "\n")、実際の呼び出し場所ではなく、関数の場所を取得します。
user2554330
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.