сценарий прерывания / выхода

87

У меня есть программа, которая выполняет некоторый анализ данных и состоит из нескольких сотен строк.

В самом начале программы я хочу провести некоторый контроль качества и, если данных недостаточно, я хочу, чтобы программа завершилась и вернулась в консоль R. В противном случае я хочу, чтобы остальная часть кода выполнялась.

Я попробовал break, browserи quitни один из них не остановить выполнение остальной части программы (и quitостанавливает выполнение, а также полностью бросить курить R, который не то , что я хочу , чтобы это произошло). Мое последнее средство - создать if-elseзаявление, как показано ниже:

 if(n < 500){}
 else{*insert rest of program here*}

но это похоже на плохую практику программирования. Я что-то упускаю?

user2588829
источник
4
quitнаверняка останавливает выполнение остальной части программы. Приведите воспроизводимый пример .
Джошуа Ульрих
@JakeBurkhead - тогда мой код выше (с пустым оператором if) лучше всего? @ Джошуа Ульрих quitвыходит из R, но я хочу вернуться к консоли R, потому что программа должна оставаться открытой для моих целей.
user2588829
Что вы имеете в виду под программой? Вы имеете в виду, что выполняете написанную вами функцию или используете скрипт?
Гэвин Симпсон
if-else, вероятно, правильный способ справиться с этим. Исключения составляют ситуации, которых не должно произойти, если все используется правильно. Если такое может случиться, и вы знаете, как с этим справиться, используйте обычный поток управления.
Мэтью

Ответы:

62

Вы можете использовать эту stopifnot()функцию, если хотите, чтобы программа выдавала ошибку:

foo <- function(x) {
    stopifnot(x > 500)
    # rest of program
}
Майкл Малик
источник
+1! Я полагаю, что функция fooдолжна вызываться в начале скрипта и содержать другой контроль валидации ...
agstudy
22
stopifnotудобен, но if(x < 500) { stop("Not enough observations in 'x': n < 500")}может быть предпочтительнее использовать подготовленный ответ . Кроме того, если это что-то для пакетного задания, полезно решить проблему без выдачи ошибки.
Гэвин Симпсон
4
Хватит путать ОП. Он хочет quit () или stop (), а не stopifnot ().
stackoverflowuser2010
10
@ stackoverflowuser2010 Он не хочет quit(см вопрос!) Я даже не думаю , что stopв stopifnotэто лучший способ справиться с этим; stopвыдает ошибку, весь скрипт просто прерывается. В то время как stopifnot(или stop) кажется наиболее предпочтительным OP для ответа, написание функции для выхода без ошибок более полезно в более широком диапазоне ситуаций. После написания множества долго выполняющихся сценариев для больших заданий анализа данных нет ничего более раздражающего, чем функции, которые выдают ошибки вместо того, чтобы обрабатывать проблему и возвращать ее чисто. Но ясно, что я не знаю, о чем говорю ...
Гэвин Симпсон
Не могли бы вы прояснить свой комментарий о выдаче ошибки @GavinSimpson? Когда я пытаюсь, stop("my message")меня распечатывают в терминале Error: "my message" Execution halted. Итак, это показывает вывод сообщения об ошибке, но вы говорите, что он не "выдает" ошибку? (т.е. он не остановит пакетное задание, которое было настроено на прерывание, если какой-либо из вызываемых им скриптов выдает ошибку). Благодаря! (Прямо сейчас я вызываю сценарий с помощью Rscript)
rrr
14

Не очень красиво, но вот способ реализовать exit()команду в R, которая мне подходит .

exit <- function() {
  .Internal(.invokeRestart(list(NULL, NULL), NULL))
}

print("this is the last message")
exit()
print("you should not see this")

Только слегка протестировано, но когда я запускаю это, я вижу, this is the last messageа затем сценарий прерывается без каких-либо сообщений об ошибке.

Йохен
источник
Обратной стороной является то, что это не разрешено для кода в пакете CRAN. Поэтому, если вы собираетесь использовать в пакете, который хотите загрузить в CRAN, в файле R CMD CHECK.
MS
1
Да, это больше похоже на системную функцию. Он может сломаться, если внутренние детали интерпретатора будут изменены, так что может быть лучше частью ядра R, а не в отдельном пакете? Я нашел это, следуя разными путями в исходном коде R, чтобы увидеть, как я могу оказаться в правильном месте, чтобы выйти из интерпретатора без сообщения об ошибке. Я нашел не так уж много способов попасть туда; вот почему я использую, .invokeRestartкоторый, в свою очередь, мне нужен .Internal.
Йохен
Ах да, кроме политик CRAN, я думаю, что это хорошее решение! Позвольте мне поставить вам +10 репутации;)
MS
странно. Я только что попробовал это, и последняя строка вывода была [1] «вы не должны видеть это» R версия 3.4.3 (2017-11-30) Платформа: x86_64-pc-linux-gnu (64-разрядная версия) Работает под: Red Hat Выпуск 6.10 Enterprise Linux Server (Сантьяго)
CodingMatters,
2
Я получил это для работыexit <- function() { invokeRestart("abort") }
капля
12

Измените конструкцию if-else:

if(n >= 500) {
  # do stuff
}
# no need for else
Томас
источник
2
достаточно просто, и я думаю, это лучшее, что я могу сделать, спасибо
user2588829
9

Изменить: похоже, что OP запускает длинный скрипт, в этом случае нужно только обернуть часть скрипта после контроля качества с помощью

if (n >= 500) {

.... long running code here

}

Если вы выходите из функции , вы, вероятно, просто захотите return(), явно или неявно.

Например, явный двойной возврат

foo <- function(x) {
  if(x < 10) {
    return(NA)
  } else {
    xx <- seq_len(x)
    xx <- cumsum(xx)
  }
  xx ## return(xx) is implied here
}

> foo(5)
[1] 0
> foo(10)
 [1]  1  3  6 10 15 21 28 36 45 55

Под return()подразумеваемым я подразумеваю, что последняя строка выглядит так, как будто вы это сделали return(xx), но немного эффективнее прекратить вызов return().

Некоторые считают использование нескольких возвратов плохим стилем; в длинных функциях отслеживание места выхода из функции может стать трудным или привести к ошибкам. Следовательно, альтернативой является наличие одной точки возврата, но изменение объекта возврата с помощью if () else ()предложения. Такая модификация foo()будет

foo <- function(x) {
  ## out is NA or cumsum(xx) depending on x
  out <- if(x < 10) {
    NA
  } else {
    xx <- seq_len(x)
    cumsum(xx)
  }
  out ## return(out) is implied here
}

> foo(5)
[1] NA
> foo(10)
 [1]  1  3  6 10 15 21 28 36 45 55
Гэвин Симпсон
источник
Я тоже думал об этом, но неясно, говорит ли OP о выходе из функции.
Thomas
Да, Томас прав - я не говорю о выходе из службы.
user2588829
1
@ user2588829 Вам было бы гораздо лучше поместить это как функцию в R, а не скрипт из 100+ строк.
Гэвин Симпсон
@GavinSimpson, я все еще новичок в R, поэтому я этого не знал. Если я определю его как функцию из 100+ строк, будет ли это лучше?
user2588829
1
@ user2588829 Да, гораздо лучше. Вы управляете аргументами функции, чтобы передать то, что необходимо. Кроме того, вместо того, чтобы искать более 100 строк кода для выполнения анализа, вы просто делаете myFun(arg1, arg2, arg3)и т. Д. Это просто гораздо лучший способ организации вещей.
Гэвин Симпсон
9

Возможно, вы просто захотите в какой-то момент прекратить выполнение длинного скрипта. т.е. например, вы хотите жестко закодировать exit () на C или Python.

print("this is the last message")
stop()
print("you should not see this")
Нетскинк
источник
1
Для этого кода я получаю сообщение об ошибке Error in eval(expr, envir, enclos) :.
jochen
2
Да, казнь действительно прекращается. По совпадению, если вы замените stop()на exit()или please.stop.now(), скрипт также остановится (разумеется, отличаются только сообщения об ошибках).
jochen
1
@jochen Добавление фразы в кавычки внутри stop()команды может помочь отличить эту «ошибку» от других сообщений. Например: stop("Manual break inserted here")может быть более информативным, чем в stop()одиночку.
Омар Вазов
3

Это старый вопрос, но чистого решения пока нет. Вероятно, это не ответ на этот конкретный вопрос, но те, кто ищет ответы о том, «как изящно выйти из сценария R», вероятно, попадут сюда. Похоже, что разработчики R забыли реализовать функцию exit (). Во всяком случае, трюк, который я нашел:

continue <- TRUE

tryCatch({
     # You do something here that needs to exit gracefully without error.
     ...

     # We now say bye-bye         
     stop("exit")

}, error = function(e) {
    if (e$message != "exit") {
        # Your error message goes here. E.g.
        stop(e)
    }

    continue <<-FALSE
})

if (continue) {
     # Your code continues here
     ...
}

cat("done.\n")

По сути, вы используете флаг, чтобы указать продолжение или нет определенного блока кода. Затем вы используете stop()функцию для передачи настроенного сообщения обработчику ошибок tryCatch()функции. Если обработчик ошибок получает ваше сообщение для корректного завершения, то он просто игнорирует ошибку и устанавливает флаг продолжения FALSE.

user2641103
источник
0

Вы можете использовать pskillфункцию из Rпакета "tools", чтобы прервать текущий процесс и вернуться в консоль. В частности, у меня есть следующая функция, определенная в файле запуска, который я использую в начале каждого скрипта. Однако вы также можете скопировать его прямо в начало вашего кода. Затем вставьте halt()в любой момент своего кода, чтобы остановить выполнение скрипта на лету. Эта функция хорошо работает в GNU / Linux и, судя по Rдокументации, должна работать и в Windows (но я не проверял).

# halt: interrupts the current R process; a short iddle time prevents R from
# outputting further results before the SIGINT (= Ctrl-C) signal is received 
halt <- function(hint = "Process stopped.\n") {
    writeLines(hint)
    require(tools, quietly = TRUE)
    processId <- Sys.getpid() 
    pskill(processId, SIGINT)
    iddleTime <- 1.00
    Sys.sleep(iddleTime)
}
Франсуа Тонно
источник
> pskill (processId, SIGINT) закрывает сеанс и даже выгоняет пользователя из RStudio. Это довольно опасно, но функционально ....
Espanta
Не знал, что это приведет к сбою RStudio, но та же проблема обсуждается в: stackoverflow.com/questions/32820534/… Однако в Linux мое решение работает нормально. Его преимущество перед stopifnot в том, что сообщение об ошибке stopifnot () не отображается.
François Tonneau
Я проверил в Windows, и он ведет себя безумно. Спасибо, в любом случае. Мне нравится пскилл.
Espanta 01
0

Вот:

if(n < 500)
{
    # quit()
    # or 
    # stop("this is some message")
}
else
{
    *insert rest of program here*
}

Оба quit()и stop(message)выйдут из вашего скрипта. Если вы загружаете свой скрипт из командной строки R, то quit()вы также выйдете из R.

stackoverflowuser2010
источник
7
Публиковать ответы, дублирующие уже опубликованные, - плохая практика.
Thomas
@Thomas, какой ответ повторяется? Я вижу только этот ответ, использующий как «стоп», так и «выход», и фактически объясняющий разницу между ними.
@Thomas: Объясните, какой именно ответ повторяет мой ответ.
stackoverflowuser2010
@Thomas: Я задал вопрос относительно вашей критики. Я жду ответа.
stackoverflowuser2010
5
Ответ @ netskink использует stop(), и OP уже указал в комментариях, что они не хотят quit()...
Бен Болкер,