有没有办法在on.exit()上运行表达式,但只有在正常完成时才会运行,而不是在出错时?

时间:2012-11-28 10:34:50

标签: r

我知道R中的函数on.exit,这很棒。它在调用函数退出时运行表达式,无论是正常还是作为错误的结果。

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

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

3 个答案:

答案 0 :(得分:13)

无论退出状态如何,都要运行on.exit()的全部内容。因此它忽略了任何错误信号。这与tryCatch函数的finally语句相当。

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

使用您的示例,即:

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

或者使用local()查看answer of Charles以获得有关此想法的精彩实现。

如果你坚持使用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包含导致错误的最后一个调用堆栈。您必须检查该堆栈中的最高呼叫是否等于当前呼叫,在这种情况下,您的呼叫很可能会抛出最后一个错误。因此,基于这种情况,你可以试着破解我自己从不使用的解决方案。

答案 1 :(得分:8)

使用您想要的代码包装所有返回函数调用的args。所以你的例子就变成了:

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&#34; GOTO认为有害&#34; ;纸)。

答案 2 :(得分: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
}