在RStudio/交互式R会话中,如何让程序在出错时停止执行?

23

在RStudio中执行一段代码块时,当出现错误时实际上并不会停止执行。例如,如果我在打开的编辑器中有以下代码:

x <- 'test'
stopifnot(is.numeric(x))
print('hello world')

当你运行它时(可以通过按下command-return或点击“Run”按钮),它会打印错误信息,但是然后继续执行print语句。

有没有办法配置RStudio,在错误处不继续执行呢?即停在上面第2行而不继续执行print语句?

编辑:刚刚意识到,如果我使用command-R在标准的R GUI中发送代码块,也会出现这种情况。


如果您将此文件作为源文件执行,则它将按照您所期望的方式工作。例如,如果您将这3行代码复制并粘贴到控制台中并按Enter键,则会分别运行每行代码。第二种模式很可能是“运行”按钮所使用的模式。 - lmo
1
@lmo 谢谢 - 问题是我认识的大多数人都是交互式编程。我正在尝试为社会科学家编写教程,让他们在代码中放置测试,并需要迎合他们的需求。 - nick_eu
总是有 tryCatchtry - lmo
6
感谢@Dason的评论,我想到了一个技巧:如果你用花括号 {} 把代码包起来,R会将整个块作为单个命令执行,这样你就能得到你想要的结果。 - lmo
1
@Dason 在数据科学领域经常遇到这个问题。人们在数据清洗和组织方面通常使用很长的脚本,并希望添加完整性检查,以确保当他们在某一点更改数据时,不会破坏其他地方的数据。只是一些小的检查,以确保观测数量仍然正确,或者唯一标识符仍然是唯一的。 - nick_eu
显示剩余3条评论
4个回答

6

解决这个问题的方法是将 R 的 'error' 选项配置为在遇到错误时运行自定义函数。

一个示例错误处理函数可以使用 Sys.sleep 暂停执行 R 并等待用户输入(例如,通过按下 ctrl-C 或 Mac 上的 R-Studio 中的 Escape 来中断正在运行的函数)。

通常情况下,R 然后会继续执行任何后续命令。然而,我们可以定义一个函数来“吃掉”所有这些,以防止它们被运行。为了使其更加简洁,我们可以在每个“被吃掉”的行之后输出“游标上移”字符序列,随着下一个输出的字符,它们将被擦除,以便未运行的行不会混乱地出现在终端/控制台上。

示例代码:

# Define our error-handling pause function:

pause=function(){
    on.exit(eat_input())
    cat("Paused: press ctrl-C (or Escape key in Rstudio) to continue\n")
    cat("(any subsequent code will be ignored)\n")
    Sys.sleep(Inf)
}

# Define our 'eat_input' function to prevent output of subsequent, not-run input:

eat_input=function(){
    cat("\033[1A")
    while((x=readline())!='')cat("\033[1A")
}

# Set the 'error' option to execute our pause function:

options(error=pause)

试试看:

print("Before the error")

# an error: we want to stop after this
xxx

# some more input: we don't want this to be run or to appear in the console
print("After the error")

为了关闭这个行为,只需将错误选项重新设置为NULL:
options(error=NULL)

4

我认为无法防止RStudio在选择一个部分并按下Ctrl+Enter时运行所有行。RStudio只是逐行运行。即使在函数内调用stopifnot(),该函数调用后的所有行仍将被评估。

如果您的目标只是在很多代码无效运行之前获得有关错误的信息,也许您可以定义一个类似于stopifnot()的函数,它将在出现错误时进入无限循环。然后,您可以按下Esc或RStudio中的停止按钮来中断程序。类似这样:

waitifnot <- function(cond) {
  if (!cond) {
    message(deparse(substitute(cond)), " is not TRUE")  
    while (TRUE) {}
  }
}

现在,您可以使用此函数运行示例代码:
x <- 'test'
waitifnot(is.numeric(x))
print('hello world')

正如预期的那样,hello world 从未被打印。您将收到一条错误消息,告诉您出了什么问题,然后程序将等待您手动中止它。
这在除交互模式以外的任何情况下都不起作用。为避免不愉快的情况,您还可以让函数在非交互模式下有所不同的反应,例如像这样:
waitifnot <- function(cond) {
  if (!cond) {
    msg <- paste(deparse(substitute(cond)), "is not TRUE")
    if (interactive()) {
      message(msg)
      while (TRUE) {}
    } else {
      stop(msg)
    }
  }
}

如果在交互模式下运行,此函数将进入无限循环。否则,它将通过调用stop()来简单地终止执行。我已经检查了在RStudio中使用Ctrl+Enter或Source按钮时(无限循环),以及在Bash命令行上使用Rscript时(程序终止)这种方式按预期工作。


3

我解决这个问题的方法是将所有可能失败的代码放在一个函数中,然后只在源文件中调用该单个函数。

因此,不要写成这样:

print('before error')
failing_function()
print('after error')

做这个

code_that_stops_with_error <- function(){
print('before error')
failing_function()
print('after error')
}
code_that_stops_with_error()

1
如@lmo所提到的,您只需要点击按钮源代码而不是选择全部+运行。实际上,您甚至不需要保存脚本文件就可以点击源代码按钮。
但问题确实是源代码将始终运行整个脚本。因此,如果您真的只想运行脚本中的一个块,则这并不能帮助您。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接