Подтвердить что ты не робот

Есть ли способ запустить выражение on.exit(), но только если оно завершено нормально, а не при ошибке?

Мне известно о функции on.exit в R, что отлично. Он выполняет выражение, когда вызывающая функция выходит из системы, как обычно, так и в результате ошибки.

Я бы хотел, чтобы выражение выполнялось только в том случае, если вызывающая функция возвращается нормально, но не в случае ошибки. У меня есть несколько точек, где функция может нормально возвращаться, и несколько точек, где она может выйти из строя. Есть ли способ сделать это?

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

Ответ 1

Просто оберните аргументы всех вызовов возвращаемой функции с кодом, который вы хотите сделать. Итак, ваш пример:

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 "GOTO считается вредной" бумагой).

Ответ 2

Вся точка on.exit() должна выполняться независимо от состояния выхода. Следовательно, он игнорирует любой сигнал ошибки. Это afaik эквивалентно оператору 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)
}

Или посмотрите на ответ Чарльза за хорошую реализацию этой идеи, используя 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 содержит последний стек вызовов, приводящий к ошибке. Вы должны проверить, соответствует ли верхний вызов в этом стеке текущему вызову, и в этом случае ваш звонок, скорее всего, запустил последнюю ошибку. Поэтому, основываясь на этом условии, вы можете попытаться взломать себе решение, которое я никогда не использовал бы сам.

Ответ 3

Немного более читаемая версия моего комментария на ответ @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
}