quebrar / sair do script

86

Tenho um programa que faz algumas análises de dados e tem algumas centenas de linhas.

Bem no início do programa, quero fazer algum controle de qualidade e, se não houver dados suficientes, quero que o programa seja encerrado e retorne ao console R. Caso contrário, quero que o resto do código seja executado.

Eu tentei break, browsere quite nenhum deles para a execução do resto do programa (e quitpara a execução, bem como fechei completamente o R, o que não é algo que eu quero que aconteça). Meu último recurso é criar uma if-elsedeclaração como a seguir:

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

mas isso parece uma prática de codificação ruim. Estou esquecendo de algo?

user2588829
fonte
4
quitcertamente interrompe a execução do resto do programa. Forneça um exemplo reproduzível .
Joshua Ulrich
@JakeBurkhead - meu código acima (com uma instrução if vazia) é a melhor maneira de ir, então? @Joshua Ulrich, quitsai de todo o R, mas quero voltar ao console do R porque o programa precisa permanecer aberto para meus propósitos.
user2588829
O que você quer dizer com programa? Você quer dizer que está executando uma função que escreveu ou está fornecendo em um script?
Gavin Simpson
if-else é provavelmente a maneira correta de lidar com isso. As exceções são para situações que não deveriam acontecer se tudo fosse usado corretamente. Se é algo que pode acontecer e você sabe como lidar com isso, use o fluxo de controle normal.
Matthew

Respostas:

62

Você pode usar a stopifnot()função se quiser que o programa produza um erro:

foo <- function(x) {
    stopifnot(x > 500)
    # rest of program
}
Michael Malick
fonte
+1! Acho que a função foodeve ser chamada no início do script e conter outro controle de validações ...
estudo em
22
stopifnoté útil, mas uma resposta criada usando if(x < 500) { stop("Not enough observations in 'x': n < 500")}pode ser preferível. Além disso, se isso for algo para um trabalho em lote, lidar com o problema sem gerar um erro é útil.
Gavin Simpson
4
Pare de tentar confundir o OP. O que ele quer é quit () ou stop (), não stopifnot ().
stackoverflowuser2010
10
@ stackoverflowuser2010 Ele não quer quit(veja a pergunta!) Eu nem sequer pensar stopde stopifnoté a melhor maneira de lidar com isso; stopgera um erro, todo o script será abortado. Embora stopifnot(ou stop) pareça ser a resposta preferida pela OP, escrever uma função para sair de forma limpa, sem erros, é mais benéfico em uma ampla gama de situações. Tendo escrito muitos scripts de longa execução para grandes trabalhos de análise de dados, nada é mais irritante do que funções que geram erros em vez de lidar com o problema e retornar de forma limpa. Mas claramente não sei do que estou falando ...
Gavin Simpson
Você pode esclarecer seu comentário sobre lançar um erro @GavinSimpson? Quando tento, stop("my message")sou impresso no terminal Error: "my message" Execution halted. Portanto, isso mostra uma saída de mensagem de erro, mas você está dizendo que não "lança" um erro? (ou seja, ele não interromperá uma tarefa em lote que foi definida para abortar se qualquer um dos scripts que ele chama lançar erros). Obrigado! (No momento estou chamando o script com Rscript)
rrr
13

Não é bonito, mas aqui está uma maneira de implementar um exit()comando em R que funcione para mim.

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

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

Apenas testado levemente, mas quando executo isso, vejo this is the last messagee, em seguida, o script é abortado sem nenhuma mensagem de erro.

Jochen
fonte
A desvantagem é que não é permitido para código em um pacote CRAN. Portanto, se você pretende usar em um pacote que deseja carregar no CRAN, um aviso será exibido no R CMD CHECK.
MS Berends
1
Sim, parece mais uma função do sistema. Ele pode quebrar se os detalhes internos do intérprete forem alterados, então pode ser melhor uma parte do núcleo R do que em um pacote separado? Descobri isso seguindo caminhos diferentes através do código-fonte R para ver como poderia terminar no lugar correto para sair do interpretador sem que uma mensagem de erro fosse emitida. Não encontrei tantas maneiras de chegar lá; é por isso que eu uso o .invokeRestartque, por sua vez, parece precisar do .Internal.
jochen,
Ah sim, além das políticas CRAN, acho que é uma boa solução! Deixe-me apresentar a você +10 repetições;)
MS Berends
esquisito. Eu tentei isso e a última linha de saída foi [1] "você não deve ver isso" R versão 3.4.3 (2017-11-30) Plataforma: x86_64-pc-linux-gnu (64 bits) Executando em: Red Hat Enterprise Linux Server versão 6.10 (Santiago)
CodingMatters
1
Tenho que trabalhar comexit <- function() { invokeRestart("abort") }
Droplet
12

Reverta sua construção if-else:

if(n >= 500) {
  # do stuff
}
# no need for else
Thomas
fonte
2
bastante simples e acho que isso pode ser o melhor que posso fazer, obrigado
user2588829
9

Edit: Parece que o OP está executando um script longo, nesse caso, basta quebrar a parte do script após o controle de qualidade com

if (n >= 500) {

.... long running code here

}

Se estiver saindo de uma função , você provavelmente só desejará return(), explícita ou implicitamente.

Por exemplo, um retorno duplo 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

Por return()estar implícito, quero dizer que a última linha é como se você tivesse feito return(xx), mas é um pouco mais eficiente deixar de fora a chamada para return().

Alguns consideram o uso de várias devoluções em um estilo ruim; em funções longas, manter o controle de onde a função sai pode se tornar difícil ou sujeito a erros. Portanto, uma alternativa é ter um único ponto de retorno, mas alterar o objeto de retorno usando a if () else ()cláusula. Essa modificação foo()seria

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
fonte
Eu também pensei nisso, mas não está claro se OP está falando sobre sair de uma função.
Thomas
Sim, Thomas está certo - não estou falando sobre interromper uma função.
user2588829
1
@ user2588829 Seria muito melhor colocar isso como uma função em R em vez de um script de mais de 100 linhas.
Gavin Simpson
@GavinSimpson oh, ainda sou novo no R, então não sabia disso. Se eu definir como uma função de mais de 100 linhas, é uma prática melhor?
user2588829
1
@ user2588829 Sim, muito melhor. Você controla os argumentos da função para que possa passar o que for necessário. Além disso, em vez de fornecer mais de 100 linhas de código para executar a análise, você apenas faz, myFun(arg1, arg2, arg3)etc. É apenas uma maneira muito melhor de organizar as coisas.
Gavin Simpson
9

Talvez você apenas queira parar de executar um script longo em algum momento. ie. como você deseja codificar permanentemente um exit () em C ou Python.

print("this is the last message")
stop()
print("you should not see this")
netskink
fonte
1
Para este código, recebo a mensagem de erro Error in eval(expr, envir, enclos) :.
jochen
2
Sim, a execução realmente pára. Coincidentemente, se você substituir stop()por exit()ou please.stop.now(), o script também será interrompido (apenas as mensagens de erro são obviamente diferentes).
jochen
1
@jochen Adicionar uma frase entre aspas dentro do stop()comando pode ajudar a distinguir esse "erro" de outras mensagens. Por exemplo: stop("Manual break inserted here")pode ser mais informativo do que stop()sozinho.
Omar Wasow
3

Esta é uma questão antiga, mas ainda não há uma solução limpa. Isso provavelmente não está respondendo a esta pergunta específica, mas aqueles que procuram respostas sobre 'como sair de um script R normalmente' provavelmente chegarão aqui. Parece que os desenvolvedores do R se esqueceram de implementar uma função exit (). De qualquer forma, o truque que encontrei é:

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

Basicamente, você usa um sinalizador para indicar a continuação ou não de um bloco de código especificado. Em seguida, você usa a stop()função para passar uma mensagem personalizada para o manipulador de erros de uma tryCatch()função. Se o manipulador de erros receber sua mensagem para sair normalmente, ele simplesmente ignora o erro e define o sinalizador de continuação como FALSE.

user2641103
fonte
0

Você pode usar a pskillfunção do Rpacote "ferramentas" para interromper o processo atual e retornar ao console. Concretamente, tenho a seguinte função definida em um arquivo de inicialização que forneço no início de cada script. Você também pode copiá-lo diretamente no início do seu código. Em seguida, insira halt()em qualquer ponto do código para interromper a execução do script imediatamente. Esta função funciona bem no GNU / Linux e, a julgar pela Rdocumentação, também deve funcionar no Windows (mas não verifiquei).

# 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
fonte
> pskill (processId, SIGINT) fecha a sessão e até mesmo chuta o usuário para fora do RStudio. É muito perigoso, mas funcional ....
Espanta
Não sabia que iria travar o RStudio, mas o mesmo problema é discutido em: stackoverflow.com/questions/32820534/… No linux, porém, minha solução funciona bem. Sua vantagem sobre stopifnot é que a mensagem de erro stopifnot () não é exibida.
François Tonneau
Verifiquei no Windows e ele se comporta maluco. Obrigado mesmo assim. Eu gosto do pskill.
Espanta
0

Aqui:

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

Ambos quit()e stop(message)sairão do seu script. Se você estiver fornecendo seu script a partir do prompt de comando R, quit()sairá do R também.

stackoverflowuser2010
fonte
7
É uma má prática postar respostas que duplicam as já postadas.
Thomas,
@Thomas qual resposta isso duplica? Eu vejo apenas esta resposta usando parar e sair, e realmente explicando a diferença entre eles.
@Thomas: Explique exatamente qual resposta minha resposta duplica.
stackoverflowuser2010
@Thomas: Eu fiz uma pergunta em relação às suas críticas. Estou esperando que você responda.
stackoverflowuser2010
5
A resposta de @netskink usa stop() e OP já indicou nos comentários que eles não querem quit()...
Ben Bolker