debugging 使用trace跳过R函数中的一行

wgmfuz8q  于 2023-03-30  发布在  其他
关注(0)|答案(2)|浏览(186)

假设我有一个函数,我想稍微调整一下它的行为(比如跳过某行)
1.复制粘贴函数并更改。
1.使用debug以交互方式更改代码。
1.使用trace注入自己的代码。
我只对这篇文章的选项3感兴趣。
例如,让我们假设下面的代码非常简单:

f <- function(dbg) {
  x <- rnorm(1)
  if (dbg) {
     cat("Message 1\n")
  }
  if (x < 0 & dbg) {
     cat("Message 2\n")
  } 
  x
}

如果(不管什么原因)我想跳过Message 2(但保留Message 1),我可以使用trace如下:

set.seed(12231)
untrace(f)
f(TRUE)
# Message 1
# Message 2
# [1] -0.5787277

trace(f, quote(dbg <- FALSE), at = 4, print = FALSE)
set.seed(12231)
f(TRUE)
# Message 1
# [1] -0.5787277

现在假设一个稍微不同的函数g

g <- function() {
  x <- rnorm(1)
  cat("Message\n")
  cat("Message 2\n")
  x
}
  • 从概念上讲 * 我想做的事情是:
trace(g, quote(if(FALSE)), at = 4, print = FALSE)

但这显然行不通。
有没有一种方法可以使用trace跳过某些行?或者是我唯一的选择来复制,粘贴和编辑函数?

fkvaft9z

fkvaft9z1#

实际上,trace所做的(对于常规函数)就是在函数体内的所需位置插入对.doTrace的调用。我们可以手动执行相同的操作以达到完全相同的效果。
但是在你的例子中,我们想要 remove 而不是add;我们也可以这样做:

modified_body <- body(g)
modified_body[4] <- NULL

original_body <- body(g)
body(g) <- modified_body

g()

# “untrace”:
body(g) <- original_body

我尝试将修改后的函数体作为tracer传递给trace,让它在trace中工作。不幸的是,我无法阻止原始函数在之后执行:向tracer添加显式的return()调用没有任何作用(因为.doTrace使用eval.parent来计算tracer代码,而eval内部的return不会退出调用范围)。我 * 可以 * 使用stop()而不是return()。然而,这是不希望的,因为它总是下降到 * 顶层 * 而不是g的调用者。我试图通过安装globalCallingHandler来影响这一点,但没有适当的重启我可以调用,并且无论如何,定期的重新启动会使我们直接回到g,而不是它的调用者。
我不知道这是否可以规避,甚至在原则上:它本质上需要修改活动调用堆栈-重新启动 do,但据我所知只有非常有限的方式(即通过abort重新启动删除整个调用堆栈,或通过在最内层调用帧内恢复)。

azpvetkf

azpvetkf2#

这里有几种方法,都以某种方式依赖于重新定义cat

1)将cat重新定义为list像这样重新定义cat。如果不需要Tracing消息,请添加print=FALSE参数。

trace(g, quote(cat <- list), at = 4)
## [1] "g"

g()
## Message
## Tracing g() step 4 
## [1] 0.6007088

2)检查cat arg另一种方法是:

trace(g, quote(cat <- \(x, ...) if (x != "Message 2\n") base::cat(x, ...)))
## [1] "g"

g()
## Tracing g() on entry 
## Message
## [1] 1.997213

3)计算cat调用次数设置i为0,然后每次调用cat时更新一次,如果不是第二次才执行。

trace(g, quote({ i <- 0; cat <- \(...) if ((i <<- i + 1) != 2) base::cat(...)}))
## [1] "g"

g()
## Tracing g() on entry 
## Message
## [1] 0.6007088

相关问题