przerwanie / zakończenie skryptu

85

Mam program, który analizuje dane i ma kilkaset wierszy.

Na bardzo wczesnym etapie programu chcę przeprowadzić kontrolę jakości, a jeśli nie ma wystarczającej ilości danych, chcę, aby program zakończył działanie i powrócił do konsoli R. W przeciwnym razie chcę wykonać resztę kodu.

Próbowałem break, browseri quitżaden z nich nie zatrzymać wykonanie pozostałej części programu (i quitzatrzymuje wykonanie, jak również całkowicie porzuceniem R, który nie jest coś, co chcę się zdarzyć). Moją ostatecznością jest stworzenie if-elseoświadczenia jak poniżej:

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

ale wydaje się to złą praktyką kodowania. Czy coś mi brakuje?

user2588829
źródło
4
quitz całą pewnością zatrzymuje wykonywanie reszty programu. Proszę podać odtwarzalny przykład .
Joshua Ulrich
@JakeBurkhead - czy powyższy kod (z pustą instrukcją if) jest najlepszym rozwiązaniem? @Joshua Ulrich, zamyka quitcałe R, ale chcę wrócić do konsoli R, ponieważ program musi pozostać otwarty do moich celów.
user2588829
Co masz na myśli mówiąc o programie? Czy masz na myśli uruchamianie funkcji, którą napisałeś, czy też pozyskujesz ją w skrypcie?
Gavin Simpson,
if-else to prawdopodobnie właściwy sposób na rozwiązanie tego problemu. Wyjątki dotyczą sytuacji, które nie powinny mieć miejsca, jeśli wszystko jest używane poprawnie. Jeśli coś może się wydarzyć i wiesz, jak sobie z tym poradzić, użyj normalnego przepływu sterowania.
Matthew,

Odpowiedzi:

59

Możesz użyć tej stopifnot()funkcji, jeśli chcesz, aby program generował błąd:

foo <- function(x) {
    stopifnot(x > 500)
    # rest of program
}
Michael Malick
źródło
+1! Myślę, że funkcja foopowinna się nazywać początkiem skryptu i zawierać inne kontrolki walidacji ...
agstudy
22
stopifnotjest przydatne, ale if(x < 500) { stop("Not enough observations in 'x': n < 500")}preferowana może być spreparowana odpowiedź przy użyciu . Ponadto, jeśli jest to coś dla zadania wsadowego, przydatne jest rozwiązanie problemu bez zgłaszania błędu.
Gavin Simpson
4
Przestań próbować zmylić OP. To, czego chce, to quit () lub stop (), a nie stopifnot ().
stackoverflowuser2010
10
@ stackoverflowuser2010 On nie chce quit(patrz pytanie!) Nawet nie myślę stopo stopifnotnajlepszym sposobie rozwiązania tego problemu ; stopzgłasza błąd, cały skrypt zostanie po prostu przerwany. Chociaż stopifnot(lub stop) wydaje się być najbardziej lubianym przez Answer OP, napisanie funkcji, która kończy się czysto, bez błędów, jest bardziej korzystne w szerszym zakresie sytuacji. Po napisaniu wielu długotrwałych skryptów do zadań analizy dużych danych, nic nie jest bardziej irytujące niż funkcje, które generują błędy zamiast obsługiwać problem i zwracać je w sposób czysty. Ale najwyraźniej nie wiem, o czym mówię ...
Gavin Simpson,
Czy możesz wyjaśnić swój komentarz dotyczący zgłaszania błędu @GavinSimpson? Kiedy próbuję, stop("my message")pojawia się wydruk na terminalu Error: "my message" Execution halted. Więc to pokazuje wyjście komunikatu o błędzie, ale czy twierdzisz, że nie „zgłasza” błędu? (tj. nie zatrzyma zadania wsadowego, które zostało ustawione na przerwanie, jeśli którykolwiek ze skryptów, które wywołuje, zgłasza błędy). Dzięki! (Teraz jestem wywołanie skryptu z Rscript)
rrr
13

Niezbyt ładne, ale tutaj jest sposób na zaimplementowanie exit()polecenia w R, które działa dla mnie.

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

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

Tylko lekko przetestowane, ale kiedy to uruchomię, widzę, this is the last messagea następnie skrypt przerywa pracę bez żadnego komunikatu o błędzie.

jochen
źródło
Wadą jest to, że nie jest dozwolony kod w pakiecie CRAN. Więc jeśli zamierzasz użyć w pakiecie, który chcesz przesłać do CRAN, wyświetli ostrzeżenie w pliku R CMD CHECK.
MS Berends
1
Tak, to bardziej przypomina funkcję systemową. Może się zepsuć, jeśli wewnętrzne szczegóły interpretera zostaną zmienione, więc może lepiej być częścią rdzenia R niż w osobnym pakiecie? Znalazłem to, podążając różnymi ścieżkami w kodzie źródłowym R, aby zobaczyć, jak mogę znaleźć się we właściwym miejscu, aby wyjść z interpretera bez wysyłania komunikatu o błędzie. Nie znalazłem zbyt wielu sposobów, aby się tam dostać; dlatego używam, .invokeRestartktóry z kolei wydaje się potrzebować pliku .Internal.
jochen
O tak, poza polityką CRAN, myślę, że to fajne rozwiązanie! Pozwól, że przedstawię ci +10 rep;)
MS Berends
dziwne. Właśnie wypróbowałem to, a ostatnią linią wyjściową było [1] „nie powinieneś widzieć tego” R wersja 3.4.3 (2017-11-30) Platforma: x86_64-pc-linux-gnu (64-bit) Działa pod: Red Hat Enterprise Linux Server, wersja 6.10 (Santiago)
CodingMatters
@aspiringGuru Właśnie próbowałem i nadal działa. Czy uruchomiłeś polecenia jako skrypt? Jeśli uruchomisz je jeden po drugim w linii poleceń ( np. Używając kopiowania i wklejania), to oczywiście exit()nie możesz zapobiec uruchomieniu następnego polecenia (które nie zostało jeszcze wprowadzone) ...
jochen
12

Odwróć swoją konstrukcję if-else:

if(n >= 500) {
  # do stuff
}
# no need for else
Tomasz
źródło
2
dość proste i myślę, że to może być najlepsze, co mogę zrobić, dzięki
user2588829
9

Edycja: wydaje się, że OP uruchamia długi skrypt, w takim przypadku wystarczy owinąć część skryptu po kontroli jakości

if (n >= 500) {

.... long running code here

}

Jeśli wyjdziesz z funkcji , prawdopodobnie będziesz po prostu chciał return(), jawnie lub niejawnie.

Na przykład jawny podwójny powrót

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

Mówiąc return()wprost, mam na myśli, że ostatnia linia wygląda tak, jakbyś skończył return(xx), ale nieco bardziej efektywne jest przerwanie wywołania return().

Niektórzy uważają, że używanie wielu zwrotów jest złym stylem; w długich funkcjach śledzenie, gdzie kończy się funkcja, może być trudne lub podatne na błędy. Stąd alternatywą jest posiadanie pojedynczego punktu powrotu, ale zmiana obiektu zwracanego za pomocą if () else ()klauzuli. Taka modyfikacja foo()byłaby

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
źródło
Też o tym myślałem, ale nie jest jasne, że OP mówi o wyrwaniu się z funkcji.
Thomas
Tak, Thomas ma rację - nie mówię o wyrwaniu się z funkcji.
user2588829
1
@ user2588829 Znacznie lepiej byłoby umieścić to jako funkcję w języku R niż ponad 100-liniowy skrypt.
Gavin Simpson,
@GavinSimpson oh, wciąż jestem nowy w R, więc tego nie wiedziałem. Jeśli zdefiniuję to jako funkcję ponad 100 linii, czy to będzie lepsza praktyka?
user2588829
1
@ user2588829 Tak, znacznie lepiej. Kontrolujesz argumenty funkcji, więc możesz przekazać to, co jest potrzebne. Poza tym, zamiast pozyskiwać ponad 100 wierszy kodu do przeprowadzenia analizy, którą właśnie wykonujesz, myFun(arg1, arg2, arg3)itd. Jest to po prostu znacznie lepszy sposób organizowania rzeczy.
Gavin Simpson
9

Być może w pewnym momencie chcesz po prostu przestać wykonywać długi skrypt. to znaczy. tak jak chcesz na stałe zakodować exit () w C lub Pythonie.

print("this is the last message")
stop()
print("you should not see this")
netskink
źródło
1
W przypadku tego kodu otrzymuję komunikat o błędzie Error in eval(expr, envir, enclos) :.
jochen
2
Tak, egzekucja rzeczywiście się kończy. Przypadkowo, jeśli zastąpi stop()się exit()lub please.stop.now()skrypt zatrzymuje się (tylko komunikaty o błędach są oczywiście różne).
jochen
1
@jochen Dodanie cytowanej frazy w stop()poleceniu może pomóc odróżnić ten „błąd” od innych komunikatów. Na przykład: stop("Manual break inserted here")może być bardziej pouczający niż stop()sam.
Omar Wasow
3

To stare pytanie, ale nie ma jeszcze czystego rozwiązania. To prawdopodobnie nie jest odpowiedzią na to konkretne pytanie, ale ci, którzy szukają odpowiedzi na temat „jak wdzięcznie wyjść ze skryptu R”, prawdopodobnie wylądują tutaj. Wygląda na to, że programiści R zapomnieli o zaimplementowaniu funkcji exit (). W każdym razie sztuczka, którą znalazłem, to:

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

Zasadniczo używasz flagi, aby wskazać kontynuację lub nie określonego bloku kodu. Następnie używasz stop()funkcji do przekazania dostosowanego komunikatu do programu obsługi błędów tryCatch()funkcji. Jeśli program obsługi błędów otrzyma wiadomość, aby pomyślnie zakończyć pracę, po prostu zignoruje błąd i ustawia flagę kontynuacji na FALSE.

user2641103
źródło
0

Możesz użyć pskillfunkcji z Rpakietu "tools", aby przerwać bieżący proces i powrócić do konsoli. Konkretnie, mam następującą funkcję zdefiniowaną w pliku startowym, który pobieram na początku każdego skryptu. Możesz jednak skopiować go bezpośrednio na początku swojego kodu. Następnie wstaw halt()w dowolnym miejscu kodu, aby zatrzymać wykonywanie skryptu w locie. Ta funkcja działa dobrze w systemie GNU / Linux i sądząc po Rdokumentacji, powinna również działać w systemie Windows (ale nie sprawdzałem).

# 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
źródło
> pskill (processId, SIGINT) zamyka sesję i nawet wyrzuca użytkownika z RStudio. Jest dość niebezpieczny, ale funkcjonalny…
Espanta,
Nie wiedziałem, że spowoduje to awarię RStudio, ale ten sam problem jest omawiany na: stackoverflow.com/questions/32820534/… Jednak na Linuksie moje rozwiązanie działa dobrze. Jego przewaga nad stopifnot polega na tym, że komunikat o błędzie stopifnot () nie pojawia się.
François Tonneau
Sprawdziłem w systemie Windows i zachowuje się szalony. W każdym razie dzięki. Podoba mi się pskill.
Espanta
0

Tutaj:

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

Obie quit()i stop(message)zamkną twój skrypt. Jeśli pozyskujesz skrypt z wiersza polecenia języka R, quit()zamkniesz również program R.

stackoverflowuser2010
źródło
7
Publikowanie odpowiedzi, które są duplikatami już opublikowanych, jest złą praktyką.
Thomas
@Thomas Która odpowiedź to duplikat? Widzę tylko tę odpowiedź, używając zarówno stop, jak i quit, i faktycznie wyjaśniając różnicę między nimi.
@Thomas: Wyjaśnij dokładnie, jaka odpowiedź się powtarza.
stackoverflowuser2010
@Thomas: Zadałem pytanie dotyczące twojej krytyki. Czekam, aż odpowiesz.
stackoverflowuser2010
5
Odpowiedź @ netskink używa stop(), a OP już wskazał w komentarzach, że nie chcą quit()...
Ben Bolker