Is there a way to run an expression on.exit() but only if completes normally, not on error?

后端 未结 3 1302
情深已故
情深已故 2021-01-08 00:48

I\'m aware of the function on.exit in R, which is great. It runs the expression when the calling function exits, either normally or as the result of an error.

相关标签:
3条回答
  • 2021-01-08 01:19

    Bit more readable version of my comment on @Joris' answer:

    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
    }
    
    0 讨论(0)
  • 2021-01-08 01:26

    The whole point of on.exit() is exactly to be run regardless of the exit status. Hence it disregards any error signal. This is afaik equivalent to the finally statement of the tryCatch function.

    If you want to run code only on normal exit, simply put it at the end of your code. Yes, you'll have to restructure it a bit using else statements and by creating only 1 exit point, but that's considered good coding practice by some.

    Using your example, that would be:

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

    Or see the answer of Charles for a nice implementation of this idea using local().

    If you insist on using on.exit(), you can gamble on the working of the traceback mechanism to do something like this :

    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 contains the last call stack resulting in an error. You have to check whether the top call in that stack is equal to the current call, and in that case your call very likely threw the last error. So based on that condition you can try to hack yourself a solution I'd never use myself.

    0 讨论(0)
  • 2021-01-08 01:28

    Just wrap the args of all your return function calls with the code that you want done. So your example becomes:

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

    Or just make each then clause into two statements. Using on.exit to lever some code into a number of places is going to cause spooky action-at-a-distance problems and make the baby Dijkstra cry (read Dijkstra's "GOTO considered harmful" paper).

    0 讨论(0)
提交回复
热议问题