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

Mat*_*wle 15 r

我知道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 )
}
Run Code Online (Sandbox Code Playgroud)

Jor*_*eys 13

整点on.exit()是完全不管的退出状态下运行.因此它忽略了任何错误信号.这finally与tryCatch函数的语句相当.

如果您只想在正常退出时运行代码,只需将其放在代码的末尾即可.是的,您将不得不使用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)
}
Run Code Online (Sandbox Code Playgroud)

或者查看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)
}
Run Code Online (Sandbox Code Playgroud)

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

  • 还想到它,使用`local({})`而不是创建然后调用一个函数会看起来更清晰 - 例如`f = function(){myvar = 1; 本地({某些可能错误的代码}); 我的代码运行成功}`.`return`将从本地范围返回,而不是函数.作为一个额外的好处 - 传递`environment()`作为第二个arg到`local`也运行父作用域中的嵌套代码,因此外部作用域中的不可访问变量没有问题. (2认同)

Spa*_*man 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) )
}
Run Code Online (Sandbox Code Playgroud)

或者只是将每个then子句分成两个语句.使用on.exit一些代码进入许多地方会引起一些远距离的怪异行为问题并使婴儿Dijkstra哭泣(阅读Dijkstra的"GOTO认为有害"论文).


Cha*_*les 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
}
Run Code Online (Sandbox Code Playgroud)