16

我知道on.exitR 中的功能,这很棒。它在调用函数正常退出或作为错误结果退出时运行表达式。

我想要的是表达式仅在调用函数正常返回时运行,但在出现错误的情况下不运行。我有多个函数可以正常返回的点,以及多个可能失败的点。有没有办法做到这一点?

myfunction = function() {
     ...
     on.exit( if (just exited normally without error) <something> )
     ...
     if (...) then return( point 1 )
     ...
     if (...) then return( point 2 )
     ...
     if (...) then return( point 3 )
     ...
     return ( point 4 )
}
4

3 回答 3

14

不管退出状态如何,整个点on.exit()就是要运行。因此它忽略任何错误信号。这finally与 tryCatch 函数的语句等效。

如果您只想在正常退出时运行代码,只需将其放在代码的末尾即可。是的,您必须使用else语句和仅创建 1 个退出点对其进行一些重构,但这被某些人认为是良好的编码实践。

使用您的示例,那将是:

myfunction = function() {
     ...
     if (...) then out <- point 1 
     ...
     else if (...) then out <- point 2 
     ...
     else if (...) then out <- point 3 
     ...
     else out <-  point 4 

     WhateverNeedsToRunBeforeReturning

     return(out)
}

或者查看Charles 的答案,使用local().

如果你坚持使用on.exit(),你可以赌回溯机制的工作来做这样的事情:

test <- function(x){
  x + 12
}                               

myFun <- function(y){
    on.exit({

        err <- if( exists(".Traceback")){
           nt <- length(.Traceback)        
           .Traceback[[nt]] == sys.calls()[[1]]
        } else {FALSE}

        if(!err) print("test")
    })  
    test(y)
}

.Traceback包含导致错误的最后一个调用堆栈。您必须检查该堆栈中的顶部调用是否等于当前调用,在这种情况下,您的调用很可能引发了最后一个错误。因此,基于这种情况,您可以尝试破解自己从未使用过的解决方案。

于 2012-11-28T11:00:55.967 回答
8

只需使用您想要完成的代码包装所有返回函数调用的参数。所以你的例子变成:

foo = function(thing){do something; return(thing)}
myfunction = function() {
     ...
     if (...) then return( foo(point 1) )
     ...
     if (...) then return( foo(point 2) )
     ...
     if (...) then return( foo(point 3) )
     ...
     return ( foo(point 4) )
}

或者只是将每个then子句变成两个语句。使用on.exit将一些代码杠杆到许多地方会导致幽灵般的远距离动作问题并使婴儿 Dijkstra 哭泣(阅读 Dijkstra 的“GOTO 认为有害”论文)。

于 2012-11-28T14:03:05.527 回答
6

我对@Joris 回答的评论更具可读性:

f = function() {
  ret = local({
    myvar = 42
    if (runif(1) < 0.5)
      return(2)
    stop('oh noes')
  }, environment())
  # code to run on success...
  print(sprintf('myvar is %d', myvar))
  ret
}
于 2012-11-28T14:27:48.263 回答