Làm thế nào để có được số dòng của một cuộc gọi hàm trong R?


8

Đối với mục đích gỡ lỗi, tôi muốn in số dòng (và tên hàm) của vị trí mà hàm hiện tại được gọi từ đó. Làm thế nào để tôi có được điều này trong R?

Tôi đã thấy một giải pháp lấy tên tệp nguồn Nhưng làm thế nào để có được số dòng và tên hàm?]

EDIT: Tôi đã tìm thấy cách lấy dữ liệu này từ traceback()một số hình thức, tracBack có thể in nó ra, nhưng tôi không chắc làm thế nào để giải mã thông tin ra khỏi nó:

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> 

Câu trả lời:


4

Tìm thấy một giải pháp! Nhận nó từ mã của tracBack ():

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

Đã viết một hàm bao bọc đẹp cho nó:

# 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
}

Đây là cách nó được sử dụng:

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

Hạn chế duy nhất (nhỏ) là khi được gọi trong các biểu thức con như cat(caller_info("Called from %f at %s#%l\n"))hoặc cat(paste0(caller_info("Called from %f at %s#%l\n"))), R nhầm lẫn tính những thứ phụ này là cấp độ ngăn xếp, làm cho nó rối tung lên. Vì vậy, tốt hơn nên tránh sử dụng trình bao bọc này trong các biểu thức.


Đẹp! Tôi không biết về đối số tùy chọn .traceback().
user2554330

1
Sự thay đổi duy nhất tôi muốn làm là sử dụng các chức năng vắt getSrcref, getSrcFilenamegetSrcLocationthay vì làm việc trực tiếp trên các thuộc tính. Định dạng bên trong có thể thay đổi.
user2554330

@ user2554330 cảm ơn, bình luận tuyệt vời! :-) Tôi đã cập nhật trình bao bọc của mình.
TMS

0

Không có chức năng dễ dàng để cung cấp cho bạn những gì bạn yêu cầu, nhưng với mục đích gỡ lỗi, bạn có thể gọi browser()một hàm, sau đó chạy wherelệnh để xem ngăn xếp cuộc gọi hiện tại. Ví dụ: bạn có thể thấy một cái gì đó như thế này:

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)

Điều này cung cấp địa điểm cho một vài cuộc gọi, nhưng không phải tất cả trong số họ.

Nếu bạn thực sự muốn một cái gì đó in ra khi bạn đi (như macro __LINE____FILE__trong C / C ++), thì khó hơn một chút. Điều này in vị trí hiện tại:

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

Không phải tất cả các hàm đều có tên và các hàm R không biết bạn gọi chúng theo tên nào, nhưng bạn có thể thấy cuộc gọi hiện tại bằng cách sử dụng sys.call(). Vì vậy, điều này in tất cả mọi thứ:

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

mà có thể in

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

sys.call có một đối số để di chuyển lên ngăn xếp, nhưng tôi không biết cách nào để có được thông tin số dòng.

Bạn có thể nhận được vị trí bắt đầu của chức năng thực hiện cuộc gọi hiện tại bằng cách sử dụng

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

sẽ cho bạn biết mã đã thực hiện cuộc gọi, nhưng số dòng chỉ dành cho chức năng mà nó đến. Đó là một lý lẽ tốt để giữ cho các chức năng của bạn ngắn!


hmm ... điều này thật thú vị ... nhưng nó thực sự quan trọng đối với tôi để có được số dòng và chức năng của cuộc gọi của chức năng này (nơi nó được gọi từ đó). Số dòng hiện tại là rõ ràng và không thú vị.
TMS

Sau đó, sử dụng browser(), có thể được kích hoạt bởi một lỗi hoặc cảnh báo hoặc sử dụng cat("Called from ",getSrcFilename(sys.function(-1)), " line ",getSrcLocation(sys.function(-1), "line"), "\n"), sẽ cung cấp cho bạn vị trí chức năng, không phải vị trí cuộc gọi thực tế.
user2554330
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.