script di interruzione / uscita


87

Ho un programma che esegue un'analisi dei dati ed è lungo poche centinaia di righe.

All'inizio del programma, desidero eseguire un controllo di qualità e se non ci sono dati sufficienti, desidero che il programma termini e torni alla console R. Altrimenti, voglio che il resto del codice venga eseguito.

Ho provato break, browsere quite nessuno di loro fermo l'esecuzione del resto del programma (e quitinterrompe l'esecuzione di R così come completamente smettere, che non è qualcosa che voglio che accada). La mia ultima risorsa è creare una if-elsedichiarazione come di seguito:

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

ma sembra una cattiva pratica di codifica. Mi sto perdendo qualcosa?


4
quitsicuramente interrompe l'esecuzione del resto del programma. Fornisci un esempio riproducibile .
Joshua Ulrich

@ JakeBurkhead - il mio codice sopra (con un'istruzione if vuota) è il modo migliore per andare, allora? @ Joshua Ulrich, quitesce tutto da R, ma voglio tornare alla console R perché il programma deve rimanere aperto per i miei scopi.
user2588829

Cosa intendi per programma? Vuoi dire che stai eseguendo una funzione che hai scritto o stai cercando in uno script?
Gavin Simpson

if-else è probabilmente il modo corretto per gestirlo. Le eccezioni sono per situazioni che non dovrebbero verificarsi se tutto viene utilizzato correttamente. Se è qualcosa che può accadere e sai come gestirlo, usa il normale flusso di controllo.
Matthew

Risposte:


62

È possibile utilizzare la stopifnot()funzione se si desidera che il programma produca un errore:

foo <- function(x) {
    stopifnot(x > 500)
    # rest of program
}

+1! Immagino che la funzione foodovrebbe essere chiamata l'inizio dello script e contenere altri controlli di convalida ...
agstudy

22
stopifnotè utile ma if(x < 500) { stop("Not enough observations in 'x': n < 500")}potrebbe essere preferibile una risposta artigianale utilizzando . Inoltre, se questo è qualcosa per un lavoro batch, è utile gestire il problema senza generare un errore.
Gavin Simpson

4
Smettila di cercare di confondere l'OP. Quello che vuole è quit () o stop (), non stopifnot ().
stackoverflowuser2010

10
@ stackoverflowuser2010 Non vuole quit(vedi domanda!) Non ho nemmeno pensare stopdi stopifnotè il modo migliore per gestire questa situazione; stopgenera un errore, l'intero script si interromperà. Mentre stopifnot(o stop) sembra essere l'OP della risposta che piace di più, scrivere una funzione per uscire in modo pulito, senza errori, è più vantaggioso in una gamma più ampia di situazioni. Avendo scritto molti script a lunga esecuzione per lavori di analisi di dati di grandi dimensioni, niente è più fastidioso delle funzioni che generano errori invece di gestire il problema e restituire in modo pulito. Ma chiaramente non so di cosa sto parlando ...
Gavin Simpson

Puoi per favore chiarire il tuo commento sul lancio di un errore @GavinSimpson? Quando provo, stop("my message")vengo stampato nel terminale Error: "my message" Execution halted. Quindi questo mostra un messaggio di errore in uscita, ma stai dicendo che non "genera" un errore? (cioè non interromperà un lavoro batch che è stato impostato per interrompersi se uno degli script che chiama genera errori). Grazie! (In questo momento sto chiamando lo script con Rscript)
rrr

14

Non carino, ma ecco un modo per implementare un exit()comando in R che funziona per me.

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

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

Testato solo leggermente, ma quando lo eseguo, vedo this is the last messagee quindi lo script si interrompe senza alcun messaggio di errore.


Lo svantaggio è che non è consentito inserire codice in un pacchetto CRAN. Quindi, se intendi utilizzare in un pacchetto che desideri caricare su CRAN, verrà visualizzato un avviso nel file R CMD CHECK.
MS Berends

1
Sì, sembra più una funzione di sistema. Potrebbe rompersi se i dettagli interni dell'interprete vengono modificati, quindi potrebbe essere meglio una parte del core R piuttosto che in un pacchetto separato? L'ho trovato seguendo diversi percorsi attraverso il codice sorgente R per vedere come potrei finire nel posto corretto per uscire dall'interprete senza che venga emesso un messaggio di errore. Non c'erano tanti modi che ho trovato per arrivarci; questo è il motivo per cui uso .invokeRestartche poi a sua volta sembra aver bisogno del .Internal.
jochen

Oh sì, a parte le politiche CRAN, penso che sia una bella soluzione! Lascia che ti consegni una ripetizione +10;)
MS Berends

strano. Ho appena provato questa e l'ultima riga di output era [1] "non dovresti vedere questo" R versione 3.4.3 (2017-11-30) Piattaforma: x86_64-pc-linux-gnu (64-bit) In esecuzione sotto: Red Hat Enterprise Linux Server versione 6.10 (Santiago)
CodingMatters

2
Ce l'ho con cui lavorareexit <- function() { invokeRestart("abort") }
Droplet

12

Inverti la tua costruzione if-else:

if(n >= 500) {
  # do stuff
}
# no need for else

2
abbastanza semplice e immagino che questo potrebbe essere il meglio che posso fare, grazie
user2588829

9

Modifica: sembra che l'OP stia eseguendo uno script lungo, in tal caso è sufficiente avvolgere la parte dello script dopo il controllo di qualità con

if (n >= 500) {

.... long running code here

}

Se interrompi una funzione , probabilmente vorrai solo return(), esplicitamente o implicitamente.

Ad esempio, un doppio ritorno esplicito

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

Per return()essere sottinteso, intendo che l'ultima riga è come se l'avessi fatto return(xx), ma è leggermente più efficiente lasciare la chiamata a return().

Alcuni considerano l'utilizzo di più ritorni in un cattivo stile; nelle funzioni lunghe, tenere traccia di dove esce la funzione può diventare difficile o soggetto a errori. Quindi un'alternativa è avere un unico punto di ritorno, ma cambiare l'oggetto di ritorno usando la if () else ()clausola. foo()Sarebbe una tale modifica

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

Ho pensato anche a questo, ma non è chiaro che OP stia parlando di interrompere una funzione.
Thomas

Sì, Thomas ha ragione: non sto parlando di interrompere una funzione.
user2588829

1
@ user2588829 Faresti molto meglio a metterlo come una funzione in R piuttosto che in uno script di oltre 100 righe.
Gavin Simpson

@GavinSimpson oh, sono ancora nuovo su R quindi non lo sapevo. Se la definisco come una funzione di oltre 100 righe, è una pratica migliore?
user2588829

1
@ user2588829 Sì, molto meglio. Tu controlli gli argomenti della funzione in modo da poter passare ciò che è necessario. Inoltre, invece di procurarti oltre 100 righe di codice per eseguire l'analisi, fai semplicemente myFun(arg1, arg2, arg3)ecc. È solo un modo molto migliore di organizzare le cose.
Gavin Simpson

9

Forse vuoi solo interrompere l'esecuzione di uno script lungo ad un certo punto. cioè. come se volessi codificare un exit () in C o Python.

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

1
Per questo codice ottengo il messaggio di errore Error in eval(expr, envir, enclos) :.
jochen

2
Sì, l'esecuzione si ferma davvero. Casualmente, se si sostituisce stop()con exit()o please.stop.now(), anche lo script si interrompe (solo i messaggi di errore sono ovviamente diversi).
Jochen

1
@jochen L'aggiunta di una frase tra virgolette all'interno del stop()comando può aiutare a distinguere questo "errore" da altri messaggi. Ad esempio: stop("Manual break inserted here")potrebbe essere più informativo che stop()da solo.
Omar Wasow

3

Questa è una vecchia domanda, ma non esiste ancora una soluzione pulita. Questo probabilmente non risponde a questa domanda specifica, ma coloro che cercano risposte su "come uscire con grazia da uno script R" probabilmente atterreranno qui. Sembra che gli sviluppatori R abbiano dimenticato di implementare una funzione exit (). Comunque, il trucco che ho trovato è:

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

Fondamentalmente, usi un flag per indicare la continuazione o meno di un blocco di codice specificato. Quindi si utilizza la stop()funzione per passare un messaggio personalizzato al gestore degli errori di una tryCatch()funzione. Se il gestore degli errori riceve il tuo messaggio per uscire correttamente, ignora semplicemente l'errore e imposta il flag di continuazione su FALSE.


0

È possibile utilizzare la pskillfunzione nel Rpacchetto "tools" per interrompere il processo corrente e tornare alla console. In concreto, ho la seguente funzione definita in un file di avvio che provo all'inizio di ogni script. Tuttavia, puoi anche copiarlo direttamente all'inizio del codice. Quindi inserire halt()in qualsiasi punto del codice per interrompere l'esecuzione dello script al volo. Questa funzione funziona bene su GNU / Linux e, a giudicare dalla Rdocumentazione, dovrebbe funzionare anche su Windows (ma non ho controllato).

# 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) chiude la sessione ed esclude anche l'utente da RStudio. È abbastanza pericoloso ma funzionale ....
Espanta

Non sapevo che sarebbe andato in crash RStudio, ma lo stesso problema è discusso in: stackoverflow.com/questions/32820534/… Su Linux, però, la mia soluzione funziona bene. Il suo vantaggio rispetto a stopifnot è che il messaggio di errore stopifnot () non viene visualizzato.
François Tonneau

Ho controllato su Windows e si comporta da matto. Grazie comunque. Mi piace lo pskill.
Espanta

0

Qui:

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

Entrambi quit()e stop(message)chiuderanno il tuo script. Se stai recuperando lo script dal prompt dei comandi R, quit()uscirai anche da R.


7
È una cattiva pratica pubblicare risposte che duplicano quelle già pubblicate.
Thomas

@Thomas quale risposta duplica? Vedo solo questa risposta usando sia stop che quit, e spiegando effettivamente la differenza tra loro.

@Thomas: Spiega esattamente quale risposta duplica la mia risposta.
stackoverflowuser2010

@Thomas: ho posto una domanda riguardo alle tue critiche. Sto aspettando che tu risponda per favore.
stackoverflowuser2010

5
La risposta di @ netskink utilizza stop()e OP ha già indicato nei commenti che non vogliono quit()...
Ben Bolker,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.