【问题标题】:break/exit script中断/退出脚本
【发布时间】:2013-07-24 02:56:22
【问题描述】:

我有一个程序可以进行一些数据分析,它有几百行长。

在程序的早期,我想做一些质量控制,如果没有足够的数据,我希望程序终止并返回 R 控制台。否则,我希望执行其余代码。

我尝试过breakbrowserquit,但它们都没有停止程序其余部分的执行(quit 停止执行并完全退出 R,这不是我想要发生的事情)。我最后的手段是创建一个if-else 声明如下:

 if(n < 500){}
 else{*insert rest of program here*}

但这似乎是不好的编码习惯。我错过了什么吗?

【问题讨论】:

  • quit 肯定会停止执行程序的其余部分。请提供reproducible example
  • @JakeBurkhead -- 我上面的代码(带有一个空的 if 语句)是最好的方法吗? @Joshua Ulrich,quit 退出所有 R,但我想返回 R 控制台,因为该程序需要保持打开状态。
  • 程序是什么意思?您的意思是您正在运行您编写的函数还是在脚本中采购?
  • if-else 可能是处理此问题的正确方法。如果一切都正确使用,则不应发生的情况除外。如果这是可能发生的事情并且您知道如何处理它,请使用正常的控制流。

标签: r exit break


【解决方案1】:

如果您希望程序产生错误,可以使用stopifnot() 函数:

foo <- function(x) {
    stopifnot(x > 500)
    # rest of program
}

【讨论】:

  • +1!我猜函数foo应该被称为脚本的开头并包含其他验证控件...
  • stopifnot 很方便,但使用if(x &lt; 500) { stop("Not enough observations in 'x': n &lt; 500")} 精心制作的回复可能是首选。此外,如果这适用于批处理作业,那么在 不引发错误的情况下处理问题很有用。
  • 停止试图混淆 OP。他要的是quit()或stop(),而不是stopifnot()。
  • @stackoverflowuser2010 他不想要quit(见问题!)我什至不认为stopifnotstop 是处理这个问题的最佳方式; stop 抛出错误,整个脚本将中止。虽然 stopifnot(或 stop)似乎是 OP 最喜欢的答案,但编写一个干净、无错误地退出的函数在更广泛的情况下更有益。为大型数据分析作业编写了大量长时间运行的脚本后,没有什么比抛出错误而不是处理问题并干净返回的函数更令人讨厌了。但显然我不知道我在说什么......
  • 您能否澄清您对@GavinSimpson 引发错误的评论?当我尝试stop("my message") 时,我会打印到终端Error: "my message" Execution halted。所以这显示了错误消息输出,但你是说它不会“抛出”错误吗? (即,如果它调用的任何脚本抛出错误,它不会停止已设置为中止的批处理作业)。谢谢! (现在我用 Rscript 调用脚本)
【解决方案2】:

也许您只是想在某个时候停止执行长脚本。 IE。就像你想用 C 或 Python 硬编码一个 exit() 一样。

print("this is the last message")
stop()
print("you should not see this")

【讨论】:

  • 对于此代码,我收到错误消息Error in eval(expr, envir, enclos) :
  • 是的,执行确实停止了。巧合的是,如果将stop() 替换为exit()please.stop.now(),脚本也会停止(当然只有错误消息不同)。
  • @jochen 在stop() 命令中添加带引号的短语可以帮助将此“错误”与其他消息区分开来。例如:stop("Manual break inserted here") 可能比单独的stop() 提供更多信息。
【解决方案3】:

已编辑。感谢@Droplet,他找到了一种无需.Internal() 即可完成这项工作的方法:这是在R 中实现exit() 命令的一种方法。

exit <- function() { invokeRestart("abort") }    

print("this is the last message")
exit()
print("you should not see this")

只进行了轻微测试,但是当我运行它时,我看到 this is the last message 然后脚本中止,没有任何错误消息。

以下是我原始答案中更丑的版本。

exit <- function() {
  .Internal(.invokeRestart(list(NULL, NULL), NULL))
}

【讨论】:

  • 缺点是它不允许用于 CRAN 包中的代码。因此,如果您打算在要上传到 CRAN 的包中使用,它将在 R CMD CHECK 中产生警告。
  • 是的,这看起来更像是一个系统函数。如果解释器的内部细节发生变化,它可能会中断,所以作为 R 核心的一部分而不是单独的包可能更好?我通过遵循 R 源代码的不同路径发现了这一点,以查看如何在正确的位置退出解释器而不会发出错误消息。我没有找到很多方法可以到达那里。这就是我使用.invokeRestart 的原因,而后者似乎又需要.Internal
  • 哦,是的,除了 CRAN 政策,我认为这是一个不错的解决方案!让我为您提供 +10 代表 ;)
  • 很奇怪。我刚试过这个,最后一个输出行是 [1] “you should not see this” R version 3.4.3 (2017-11-30) Platform: x86_64-pc-linux-gnu (64-bit) Running under: Red Hat Enterprise Linux Server 6.10 版(圣地亚哥)
  • 我让它和exit &lt;- function() { invokeRestart("abort") }一起工作
【解决方案4】:

反转你的 if-else 结构:

if(n >= 500) {
  # do stuff
}
# no need for else

【讨论】:

  • 很简单,我想这可能是我能做的最好的了,谢谢
【解决方案5】:

编辑: 似乎 OP 正在运行一个很长的脚本,在这种情况下,只需要在质量控制之后

包装脚本的一部分
if (n >= 500) {

.... long running code here

}

如果中断一个函数,您可能只需要return(),无论是显式还是隐式。

例如,显式的双重返回

foo <- function(x) {
  if(x < 10) {
    return(NA)
  } else {
    xx <- seq_len(x)
    xx <- cumsum(xx)
  }
  xx ## return(xx) is implied here
}

> foo(5)
[1] 0
> foo(10)
 [1]  1  3  6 10 15 21 28 36 45 55

隐含return(),我的意思是最后一行就像你已经完成了return(xx),但不调用return()会更有效。

有些人考虑使用多次返回不好的风格;在长函数中,跟踪函数退出的位置可能变得困难或容易出错。因此,另一种方法是使用单个返回点,但使用 if () else () 子句更改返回对象。对foo() 的这种修改将是

foo <- function(x) {
  ## out is NA or cumsum(xx) depending on x
  out <- if(x < 10) {
    NA
  } else {
    xx <- seq_len(x)
    cumsum(xx)
  }
  out ## return(out) is implied here
}

> foo(5)
[1] NA
> foo(10)
 [1]  1  3  6 10 15 21 28 36 45 55

【讨论】:

  • 我也想过这个,但不清楚 OP 是否在谈论突破一个函数。
  • 是的,Thomas 是对的——我不是在谈论打破功能。
  • @user2588829 最好将其作为 R 中的函数而不是 100 多行脚本。
  • @GavinSimpson 哦,我还是 R 新手,所以我不知道。如果我将其定义为 100+ 行函数是更好的做法吗?
  • @user2588829 是的,好多了。您可以控制函数的参数,以便传入需要的参数。此外,无需采购 100 多行代码来运行分析,您只需执行 myFun(arg1, arg2, arg3) 等。这只是一种更好的组织方式。
【解决方案6】:

这是一个老问题,但还没有一个干净的解决方案。这可能没有回答这个特定问题,但那些寻找“如何优雅地退出 R 脚本”的答案的人可能会在这里找到答案。似乎 R 开发人员忘记实现 exit() 函数。无论如何,我发现的窍门是:

continue <- TRUE

tryCatch({
     # You do something here that needs to exit gracefully without error.
     ...

     # We now say bye-bye         
     stop("exit")

}, error = function(e) {
    if (e$message != "exit") {
        # Your error message goes here. E.g.
        stop(e)
    }

    continue <<-FALSE
})

if (continue) {
     # Your code continues here
     ...
}

cat("done.\n")

基本上,您使用标志来指示指定代码块是否继续。然后使用stop() 函数将自定义消息传递给tryCatch() 函数的错误处理程序。如果错误处理程序收到您的消息以正常退出,那么它只是忽略错误并将继续标志设置为FALSE

【讨论】:

    【解决方案7】:

    这里:

    if(n < 500)
    {
        # quit()
        # or 
        # stop("this is some message")
    }
    else
    {
        *insert rest of program here*
    }
    

    quit()stop(message) 都将退出您的脚本。如果您从 R 命令提示符获取脚本,那么 quit() 也会从 R 中退出。

    【讨论】:

    • 发布与已经发布的答案重复的答案是一种不好的做法。
    • @Thomas 这重复了哪个答案?我只看到这个答案同时使用了停止和退出,并实际上解释了它们之间的区别。
    • @Thomas:准确解释我的答案重复了什么答案。
    • @Thomas:我就你的批评提出了一个问题。我在等你回答。
    • @netskink 的回答使用stop(),并且 OP 已经在 cmets 中指出他们不想要quit() ...
    【解决方案8】:

    我遇到了类似的问题:退出当前函数,但不想完成其余代码。 最后我通过一个只运行一次的 for() 循环解决了这个问题。在 for 循环中,您可以设置几个不同的条件来退出当前循环(函数)。

      for (i in T) {
        print('hello')
        if (leave.condition) next
        print('good bye')
      }
    

    【讨论】:

      【解决方案9】:

      您可以使用R“工具”包中的pskill函数来中断当前进程并返回控制台。具体来说,我在每个脚本开头的启动文件中定义了以下函数。但是,您也可以直接在代码开头复制它。然后在代码中的任意位置插入halt() 以即时停止脚本执行。这个函数在 GNU/Linux 上运行良好,从 R 文档来看,它也应该在 Windows 上运行(但我没有检查)。

      # halt: interrupts the current R process; a short iddle time prevents R from
      # outputting further results before the SIGINT (= Ctrl-C) signal is received 
      halt <- function(hint = "Process stopped.\n") {
          writeLines(hint)
          require(tools, quietly = TRUE)
          processId <- Sys.getpid() 
          pskill(processId, SIGINT)
          iddleTime <- 1.00
          Sys.sleep(iddleTime)
      }
      

      【讨论】:

      • >pskill(processId, SIGINT) 关闭会话,甚至将用户踢出 RStudio。这很危险但很实用....
      • 不知道它会导致 RStudio 崩溃,但同样的问题在以下位置进行了讨论:stackoverflow.com/questions/32820534/… 虽然在 linux 上,我的解决方案工作正常。它比 stopifnot 的优点是 stopifnot() 错误消息不会出现。
      • 我在 Windows 上检查过,它表现得很疯狂。不管怎么说,还是要谢谢你。我喜欢pskill。
      猜你喜欢
      • 2015-03-03
      • 1970-01-01
      • 1970-01-01
      • 2011-02-14
      • 1970-01-01
      • 1970-01-01
      • 2011-05-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多