добавить on.exit expr к родительскому вызову? - PullRequest
4 голосов
/ 17 декабря 2011

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

Например, допустим, что parentOnExit (expr) - это функция, реализующая это. Тогда для следующего кода:

f <- function() {
  parentOnExit(print("B"))
  print("A")
}

Я хочу, чтобы напечатали «А», затем «В».

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

foo <- function(r=NULL) {
  if (is.null(r)) {  # If we weren't passed open connection, open one
    r <- openR()
    on.exit(close(r))
  }
  bar(r=r)  # Pass the open connection down
}

Я надеялся абстрагировать эти три строки до:

r <- openIfNull(r)  # Magically call on.exit(close(r)) in scope of caller

Теперь, когда я думаю об этом, возможно, стоит повторить код, чтобы избежать чего-то слишком волшебного. Но все же мне интересно узнать ответ на мой оригинальный вопрос. Спасибо!

Ответы [ 3 ]

7 голосов
/ 08 января 2014

Я видел в этом недавнем почтовом обсуждении (https://stat.ethz.ch/pipermail/r-devel/2013-November/067874.html), что вы можете использовать do.call для этого:

f <- function() { do.call("on.exit", list(quote(cat('ONEXIT!\n'))), envir = parent.frame()); 42 }
g <- function() { x <- f(); cat('Not yet!\n'); x }
g()
#Not yet!
#ONEXIT!
#[1] 42
4 голосов
/ 18 декабря 2011

Я был заинтригован этой проблемой и попытался решить ее несколькими способами.К сожалению, они не работали.Поэтому я склонен полагать, что это не может быть сделано.... Но кто-то еще может доказать, что я не прав!

В любом случае, я решил опубликовать свои неудачные попытки, чтобы они были записаны.Я сделал их так, чтобы они печатали "ONEXIT!" после"Еще нет!"если они работали ...

1 - Во-первых, просто попробуйте оценить on.exit в родительской среде:

f <- function() { eval(on.exit(cat('ONEXIT!\n')), parent.frame()); 42 }
g <- function() { x<-f(); cat('Not yet!\n'); x }
g() # Nope, doesn't work!

Это не сработает, возможно, потому что on.exitФункция добавляет материал в текущий кадр стека, а не в текущую среду.

2 - Активизируйте игру и попытайтесь вернуть выражение, которое оценивается вызывающей стороной:

f <- function() { quote( {on.exit(cat('ONEXIT!\n')); 42}) }
g <- function() { x<-eval(f()); cat('Not yet!\n'); x }
g() # Nope, doesn't work!

Это неЭто тоже не работает, вероятно, потому что eval имеет свой собственный стековый фрейм, отличный от g.

3 - принесите мою A-игру и попробуйте положиться на ленивую оценку:

h <- function(x) sys.frame(sys.nframe())
f <- function() { h({cat('Registering\n');on.exit(cat("ONEXIT!\n"));42}) }
g <- function() { x<-f()$x; cat('Not yet!\n'); x }
g() # Worse, "ONEXIT!" is never printed...

Это возвращает окружение вызывающей стороне, и когда вызывающая сторона обращается к "x" в нем, вычисляется выражение, включающее on.exit.... Но, похоже, on.exit вообще не регистрируется в этом случае.

4 - Хм.Есть еще один способ, который все еще может работать: .Call к некоторому коду C, который вызывает on.exit.Возможно, при вызове C не будет добавлен еще один кадр стека ... Это слишком сложно для меня сейчас, чтобы проверить, но, возможно, какой-нибудь гуру RAPI / RCpp может сделать это?

1 голос
/ 18 декабря 2011

Я остаюсь в замешательстве, но если Томми не сможет этого сделать, я подозреваю, что тоже не смогу. Это делает первое задание, и так как это казалось таким простым, я подумал, что мне чего-то не хватает:

 f <- function() {
   on.exit(print("B"))
   print("A")
 }

Второе усилие:

  txtB <- textConnection("test b")
  txt <-textConnection("test A")
  f <- function(con) { df <- read.table(con); 
                  if( isOpen(txtB)){ print("B open")
                            eval( close(txtB), env=.GlobalEnv ) }
                  return(df) }
  txtB  #just to make sure it's still open
#     description            class             mode             text 
#    "\"test b\"" "textConnection"              "r"           "text" 
#          opened         can read        can write 
#        "opened"            "yes"             "no" 
  dat <- f(txt); dat
#[1] "B open"
#    V1 V2
#1 test  A
 txtB
 #Error in summary.connection(x) : invalid connection

(ОК, я отредактировал его, чтобы закрыть соединение в вызывающей среде.) Так чего мне не хватает? (Мне было непонятно, когда я проверял это, что соединения действительно имеют среды.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...