secuencia de comandos de ruptura / salida

85

Tengo un programa que hace algunos análisis de datos y tiene unos cientos de líneas.

Muy temprano en el programa, quiero hacer un control de calidad y, si no hay suficientes datos, quiero que el programa finalice y vuelva a la consola R. De lo contrario, quiero que se ejecute el resto del código.

Lo he intentado break, browsery quitninguno de ellos detener la ejecución del resto del programa (y quitdetiene la ejecución, así como dejar de fumar por completo R, que no es algo que quiero que suceda). Mi último recurso es crear una if-elsedeclaración de la siguiente manera:

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

pero eso parece una mala práctica de codificación. ¿Me estoy perdiendo de algo?

usuario2588829
fuente
4
quitciertamente detiene la ejecución del resto del programa. Proporcione un ejemplo reproducible .
Joshua Ulrich
@JakeBurkhead: ¿es mi código anterior (con una declaración if vacía) la mejor manera de hacerlo? @Joshua Ulrich, quitsale de todo R, pero quiero volver a la consola R porque el programa debe permanecer abierto para mis propósitos.
user2588829
¿A qué te refieres con programa? ¿Quiere decir que está ejecutando una función que escribió o está comprando en un script?
Gavin Simpson
if-else es probablemente la forma correcta de manejar esto. Las excepciones son para situaciones que no deberían ocurrir si todo se usa correctamente. Si es algo que puede suceder y sabe cómo manejarlo, use el flujo de control normal.
Mateo

Respuestas:

59

Puede usar la stopifnot()función si desea que el programa produzca un error:

foo <- function(x) {
    stopifnot(x > 500)
    # rest of program
}
Michael Malick
fuente
+1! Supongo que la función foodebería llamarse al comienzo del script y contener otro control de validaciones ...
agstudy
22
stopifnotes útil, pero if(x < 500) { stop("Not enough observations in 'x': n < 500")}se puede preferir una respuesta elaborada . Además, si esto es algo para un trabajo por lotes, es útil manejar el problema sin arrojar un error.
Gavin Simpson
4
Deja de intentar confundir al OP. Lo que quiere es salir () o parar (), no parar si no ().
stackoverflowuser2010
10
@ stackoverflowuser2010 Él no quiere quit(¡vea la pregunta!) Ni siquiera pienso stopen stopifnotla mejor manera de manejar esto; stoparroja un error, todo el script simplemente abortará. Si bien stopifnot(o stop) parece ser la respuesta que más le gustó a OP, escribir una función para salir limpiamente, sin errores, es más beneficioso en una gama más amplia de situaciones. Habiendo escrito muchos scripts de larga duración para trabajos de análisis de datos de gran tamaño, nada es más molesto que las funciones que arrojan errores en lugar de manejar el problema y regresar limpiamente. Pero claramente no sé de qué estoy hablando ...
Gavin Simpson
¿Puede aclarar su comentario sobre el error @GavinSimpson? Cuando lo intento, stop("my message")me imprimen en la terminal Error: "my message" Execution halted. Entonces, esto muestra una salida de mensaje de error, pero ¿estás diciendo que no "arroja" un error? (es decir, no detendrá un trabajo por lotes que se ha configurado para abortar si alguno de los scripts que llama arroja errores). ¡Gracias! (Ahora mismo estoy llamando al script con Rscript)
rrr
13

No es bonito, pero aquí hay una forma de implementar un exit()comando en R que me funciona.

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

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

Solo probado ligeramente, pero cuando ejecuto esto, veo this is the last messagey luego el script se aborta sin ningún mensaje de error.

jochen
fuente
La desventaja es que no está permitido para el código en un paquete CRAN. Entonces, si tiene la intención de usarlo en un paquete que desea cargar en CRAN, aparecerá una advertencia en el archivo R CMD CHECK.
MS Berends
1
Sí, esto se parece más a una función del sistema. Podría romperse si se cambian los detalles internos del intérprete, por lo que puede ser mejor una parte del núcleo de R en lugar de un paquete separado. Encontré esto siguiendo diferentes rutas a través del código fuente de R para ver cómo podía terminar en el lugar correcto para salir del intérprete sin que se emitiera un mensaje de error. No encontré muchas formas de llegar allí; esta es la razón por la que uso, .invokeRestartque a su vez parece necesitar el .Internal.
jochen
Oh, sí, aparte de las políticas de CRAN, ¡creo que es una buena solución! Déjame entregarte un representante de +10;)
MS Berends
extraño. Intenté esto y la última línea de salida fue [1] "no debería ver esto" R versión 3.4.3 (2017-11-30) Plataforma: x86_64-pc-linux-gnu (64-bit) Ejecutando bajo: Red Hat Enterprise Linux Server versión 6.10 (Santiago)
CodingMatters
@aspiringGuru Acabo de intentarlo y todavía me funciona. ¿Ejecutó los comandos como un script? Si los ejecuta uno tras otro en la línea de comandos ( por ejemplo, usando copiar y pegar), entonces, por supuesto exit(), no puede evitar que se ejecute el siguiente comando (que aún no se ha ingresado) ...
jochen
12

Invierta su construcción if-else:

if(n >= 500) {
  # do stuff
}
# no need for else
Thomas
fuente
2
lo suficientemente simple y creo que esto podría ser lo mejor que puedo hacer, gracias
user2588829
9

Editar: Parece que el OP está ejecutando un script largo, en ese caso, solo se necesita envolver la parte del script después del control de calidad con

if (n >= 500) {

.... long running code here

}

Si se sale de una función , probablemente solo querráreturn() , ya sea explícita o implícitamente.

Por ejemplo, un retorno doble explícito

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

Al return()estar implícito, me refiero a que la última línea es como si lo hubiera hecho return(xx), pero es un poco más eficiente dejar la llamada areturn() .

Algunos consideran que el uso de devoluciones múltiples es de mal estilo; en funciones largas, realizar un seguimiento de dónde sale la función puede resultar difícil o propenso a errores. Por lo tanto, una alternativa es tener un único punto de retorno, pero cambiar el objeto de retorno usando la if () else ()cláusula. Tal modificación foo()sería

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
Gavin Simpson
fuente
También pensé en esto, pero no está claro que OP esté hablando de romper una función.
Thomas
Sí, Thomas tiene razón, no estoy hablando de romper una función.
user2588829
1
@ user2588829 Sería mucho mejor poner eso como una función en R en lugar de un script de 100+ líneas.
Gavin Simpson
@GavinSimpson oh, todavía soy nuevo en R, así que no sabía eso. Si lo defino como una función de más de 100 líneas, ¿es mejor práctica?
user2588829
1
@ user2588829 Sí, mucho mejor. Usted controla los argumentos de la función para que pueda pasar lo que se necesita. Además, en lugar de obtener las más de 100 líneas de código para ejecutar el análisis que acaba de hacer, myFun(arg1, arg2, arg3)etc. Es una forma mucho mejor de organizar las cosas.
Gavin Simpson
9

Quizás solo desee dejar de ejecutar un script largo en algún momento. es decir. como si quisiera codificar una salida () en C o Python.

print("this is the last message")
stop()
print("you should not see this")
netskink
fuente
1
Para este código, aparece el mensaje de error Error in eval(expr, envir, enclos) :.
jochen
2
Sí, la ejecución se detiene de hecho. Casualmente, si reemplaza stop()con exit()o please.stop.now(), el script también se detiene (solo los mensajes de error son, por supuesto, diferentes).
jochen
1
@jochen Agregar una frase entre comillas dentro del stop()comando puede ayudar a distinguir este "error" de otros mensajes. Por ejemplo: stop("Manual break inserted here")podría ser más informativo que stop()solo.
Omar Wasow
3

Esta es una vieja pregunta, pero aún no existe una solución clara. Esto probablemente no responde a esta pregunta específica, pero aquellos que buscan respuestas sobre 'cómo salir con gracia de un script R' probablemente aterrizarán aquí. Parece que los desarrolladores de R se olvidaron de implementar una función exit (). De todos modos, el truco que encontré es:

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

Básicamente, usa una bandera para indicar la continuación o no de un bloque de código específico. Luego, usa la stop()función para pasar un mensaje personalizado al controlador de errores de una tryCatch()función. Si el controlador de errores recibe su mensaje para salir correctamente, simplemente ignora el error y establece el indicador de continuación en FALSE.

usuario2641103
fuente
0

Puede utilizar la pskillfunción en el Rpaquete "herramientas" para interrumpir el proceso actual y volver a la consola. Concretamente, tengo la siguiente función definida en un archivo de inicio que obtengo al principio de cada script. Sin embargo, también puede copiarlo directamente al comienzo de su código. Luego inserte halt()en cualquier punto de su código para detener la ejecución del script sobre la marcha. Esta función funciona bien en GNU / Linux y, a juzgar por la Rdocumentación, también debería funcionar en Windows (pero no lo comprobé).

# 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)
}
François Tonneau
fuente
> pskill (processId, SIGINT) cierra la sesión e incluso expulsa al usuario de RStudio. Es bastante peligroso pero funcional ....
Espanta
No sabía que bloquearía RStudio, pero el mismo problema se discute en: stackoverflow.com/questions/32820534/… Sin embargo, en Linux, mi solución funciona bien. Su ventaja sobre stopifnot es que el mensaje de error stopifnot () no aparece.
François Tonneau
Revisé Windows y se comporta como loco. Gracias de cualquier manera. Me gusta el pskill.
Espanta
0

Aquí:

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

Ambos quit()y stop(message)saldrán de su guión. Si está obteniendo su script desde el símbolo del sistema de R, también quit()saldrá de R.

stackoverflowuser2010
fuente
7
Es una mala práctica publicar respuestas que dupliquen las ya publicadas.
Thomas
@Thomas, ¿qué respuesta duplica esto? Solo veo esta respuesta usando detener y salir, y en realidad explicando la diferencia entre ellos.
@Thomas: Explique exactamente qué respuesta duplica mi respuesta.
stackoverflowuser2010
@Thomas: Le hice una pregunta sobre su crítica. Estoy esperando que por favor responda.
stackoverflowuser2010
5
La respuesta de @netskink usa stop(), y OP ya indicó en los comentarios que no quieren quit()...
Ben Bolker