ngắt / thoát tập lệnh


85

Tôi có một chương trình thực hiện một số phân tích dữ liệu và dài vài trăm dòng.

Rất sớm trong chương trình, tôi muốn thực hiện một số kiểm soát chất lượng và nếu không có đủ dữ liệu, tôi muốn chương trình kết thúc và quay lại bảng điều khiển R. Nếu không, tôi muốn phần còn lại của mã thực thi.

Tôi đã thử break, browserquitvà không ai trong số họ dừng lại việc thực hiện phần còn lại của chương trình (và quitdừng việc thực hiện cũng như hoàn toàn bỏ R, mà không phải là điều mà tôi muốn xảy ra). Phương pháp cuối cùng của tôi là tạo một if-elsetuyên bố như sau:

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

nhưng điều đó có vẻ giống như thực hành mã hóa tồi. Tui bỏ lỡ điều gì vậy?


4
quithầu hết chắc chắn dừng thực hiện phần còn lại của chương trình. Vui lòng cung cấp một ví dụ có thể lặp lại .
Joshua Ulrich

@JakeBurkhead - mã của tôi ở trên (với câu lệnh if trống) có phải là cách tốt nhất để thực hiện không? @Joshua Ulrich, quitthoát khỏi tất cả R, nhưng tôi muốn quay lại bảng điều khiển R vì chương trình cần vẫn mở cho mục đích của tôi.
user2588829

Ý bạn là gì về một chương trình? Ý bạn là bạn đang chạy một chức năng bạn đã viết hay bạn đang tìm nguồn cung cấp trong một tập lệnh?
Gavin Simpson

if-else có lẽ là cách chính xác để xử lý điều này. Ngoại lệ dành cho những tình huống không nên xảy ra nếu mọi thứ được sử dụng đúng cách. Nếu đó là điều gì đó có thể xảy ra và bạn biết cách xử lý, hãy sử dụng luồng điều khiển bình thường.
Matthew

Câu trả lời:


59

Bạn có thể sử dụng stopifnot()hàm nếu bạn muốn chương trình tạo ra lỗi:

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

! 1 Tôi đoán Chức năng foonên được gọi là sự khởi đầu của kịch bản và chứa kiểm soát kiểm chứng thực khác ...
agstudy

22
stopifnotrất tiện dụng nhưng một phản hồi thủ công bằng cách sử dụng if(x < 500) { stop("Not enough observations in 'x': n < 500")}có thể được ưu tiên hơn. Ngoài ra, nếu đây là một cái gì đó cho một công việc hàng loạt, xử lý vấn đề mà không gây ra lỗi sẽ hữu ích.
Gavin Simpson

4
Ngừng cố gắng nhầm lẫn OP. Những gì anh ta muốn là bỏ () hoặc dừng (), không phải stopifnot ().
stackoverflowuser2010

10
@ stackoverflowuser2010 Anh không muốn quit(xem câu hỏi!) Tôi thậm chí không nghĩ stopvề stopifnotlà cách tốt nhất để xử lý này; stopném một lỗi, toàn bộ tập lệnh sẽ bị hủy bỏ. Trong khi stopifnot(hoặc stop) có vẻ là Câu trả lời OP thích nhất, viết một hàm để thoát rõ ràng, không có lỗi, có lợi hơn trong nhiều tình huống hơn. Đã viết rất nhiều tập lệnh dài hạn cho các công việc phân tích dữ liệu lớn, không gì khó chịu hơn các hàm tạo lỗi thay vì xử lý vấn đề và trả về một cách sạch sẽ. Nhưng rõ ràng tôi không biết mình đang nói về điều gì ...
Gavin Simpson.

Bạn có thể vui lòng làm rõ nhận xét của mình về việc ném lỗi @GavinSimpson? Khi tôi thử, stop("my message")tôi được in vào thiết bị đầu cuối Error: "my message" Execution halted. Vì vậy, điều này hiển thị một đầu ra thông báo lỗi, nhưng bạn đang nói rằng nó không "ném" lỗi? (tức là nó sẽ không dừng một công việc hàng loạt đã được thiết lập để hủy bỏ nếu bất kỳ tập lệnh nào mà nó gọi là có lỗi). Cảm ơn! (Ngay bây giờ tôi đang gọi kịch bản bằng Rscript)
rrr

13

Không đẹp, nhưng đây là một cách để triển khai một exit()lệnh trong R phù hợp với tôi.

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

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

Chỉ được kiểm tra nhẹ, nhưng khi tôi chạy cái này, tôi thấy this is the last messagevà sau đó tập lệnh hủy bỏ mà không có bất kỳ thông báo lỗi nào.


Nhược điểm là nó không được phép cho mã trong gói CRAN. Vì vậy, nếu bạn có ý định sử dụng trong một gói mà bạn muốn tải lên CRAN, nó sẽ đưa ra một cảnh báo trong R CMD CHECK.
MS Berends

1
Có, điều này trông giống một chức năng hệ thống hơn. Nó có thể bị hỏng nếu các chi tiết bên trong của trình thông dịch bị thay đổi, vì vậy có thể tốt hơn một phần của lõi R hơn là trong một gói riêng biệt? Tôi đã tìm thấy điều này bằng cách làm theo các đường dẫn khác nhau thông qua mã nguồn R để xem cách tôi có thể kết thúc ở đúng nơi để thoát trình thông dịch mà không phát ra thông báo lỗi. Không có quá nhiều cách tôi tìm thấy để đạt được điều đó; đây là lý do tại sao tôi sử dụng .invokeRestartmà sau đó dường như cần đến .Internal.
jochen

Ồ vâng, ngoài các chính sách CRAN, tôi nghĩ đó là một giải pháp tốt! Hãy để tôi cung cấp cho bạn một đại diện 10;)
MS Berends

kỳ dị. Tôi vừa thử điều này và dòng đầu ra cuối cùng là [1] "bạn không nên thấy điều này" R phiên bản 3.4.3 (2017-11-30) Nền tảng: x86_64-pc-linux-gnu (64-bit) Đang chạy dưới: Red Hat Phiên bản Enterprise Linux Server 6.10 (Santiago)
CodingMatters

@aspiringGuru Tôi vừa thử và nó vẫn hoạt động với tôi. Bạn đã chạy các lệnh như một tập lệnh? Nếu bạn chạy chúng cái khác trong dòng lệnh ( ví dụ như sử dụng sao chép và dán), sau đó tất nhiên exit()không thể ngăn chặn các lệnh tiếp theo (mà chưa được nhập nào) chạy ...
Jochen

12

Đảo ngược cấu trúc if-else của bạn:

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

2
đủ đơn giản và tôi đoán đây có thể là tốt nhất mà tôi có thể làm, nhờ
user2588829

9

Chỉnh sửa: Có vẻ như OP đang chạy một tập lệnh dài, trong trường hợp đó, người ta chỉ cần gói phần của tập lệnh sau khi kiểm soát chất lượng với

if (n >= 500) {

.... long running code here

}

Nếu phá vỡ một chức năng , có thể bạn sẽ chỉ muốnreturn() , rõ ràng hoặc ẩn ý.

Ví dụ: lợi nhuận kép rõ ràng

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

Theo return()ngụ ý, tôi có nghĩa là dòng cuối cùng như thể bạn đã hoàn thành return(xx), nhưng sẽ hiệu quả hơn một chút nếu bỏ cuộc gọi đếnreturn() .

Một số xem xét sử dụng nhiều trả về kiểu xấu; trong các chức năng dài, việc theo dõi vị trí thoát chức năng có thể trở nên khó khăn hoặc dễ xảy ra lỗi. Do đó, một giải pháp thay thế là có một điểm trả về duy nhất, nhưng thay đổi đối tượng trả về bằng if () else ()mệnh đề. Một sửa đổi như vậy foo()sẽ là

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

Tôi cũng nghĩ đến điều này, nhưng không rõ OP đang nói về việc phá vỡ một chức năng.
Thomas

Vâng, Thomas đúng - Tôi không nói về việc phá vỡ một chức năng.
user2588829

1
@ user2588829 Tốt hơn hết bạn nên đặt nó dưới dạng một hàm trong R hơn là một tập lệnh hơn 100 dòng.
Gavin Simpson

@GavinSimpson ồ, tôi vẫn chưa quen với R nên tôi không biết điều đó. Nếu tôi định nghĩa nó là một hàm trên 100 dòng thì đó có phải là phương pháp tốt hơn không?
user2588829,

1
@ user2588829 Vâng, tốt hơn nhiều. Bạn kiểm soát các đối số của hàm để có thể truyền vào những gì cần thiết. Ngoài ra, thay vì tìm nguồn cung cấp hơn 100 dòng mã để chạy phân tích bạn vừa làm, myFun(arg1, arg2, arg3)v.v. Đó chỉ là một cách tổ chức mọi thứ tốt hơn nhiều.
Gavin Simpson

9

Có lẽ bạn chỉ muốn dừng thực thi một đoạn script dài vào một lúc nào đó. I E. giống như bạn muốn viết mã một lệnh exit () bằng C hoặc Python.

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

1
Đối với mã này, tôi nhận được thông báo lỗi Error in eval(expr, envir, enclos) :.
jochen

2
Vâng, việc thực thi thực sự dừng lại. Thật trùng hợp, nếu bạn thay thế stop()bằng exit()hoặc please.stop.now(), tập lệnh cũng dừng lại (tất nhiên chỉ có các thông báo lỗi là khác).
jochen

1
@jochen Thêm một cụm từ được trích dẫn bên trong stop()lệnh có thể giúp phân biệt "lỗi" này với các thông báo khác. Ví dụ: stop("Manual break inserted here")có thể nhiều thông tin hơn là stop()một mình.
Omar Wasow

3

Đây là một câu hỏi cũ nhưng vẫn chưa có một giải pháp sạch. Đây có lẽ không trả lời câu hỏi cụ thể này, nhưng những người đang tìm kiếm câu trả lời về 'cách thoát khỏi tập lệnh R một cách duyên dáng' có thể sẽ đến đây. Có vẻ như các nhà phát triển R đã quên triển khai một hàm exit (). Dù sao, mẹo tôi đã tìm ra là:

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

Về cơ bản, bạn sử dụng một cờ để chỉ ra sự tiếp tục hay không của một khối mã được chỉ định. Sau đó, bạn sử dụng stop()hàm để chuyển một thông báo tùy chỉnh đến trình xử lý lỗi của một tryCatch()hàm. Nếu trình xử lý lỗi nhận được thông báo của bạn để thoát một cách duyên dáng, thì nó chỉ bỏ qua lỗi và đặt cờ tiếp tục thành FALSE.


0

Bạn có thể sử dụng pskillchức năng trong Rgói "công cụ" để ngắt quá trình hiện tại và quay lại bảng điều khiển. Cụ thể, tôi có chức năng sau được xác định trong tệp khởi động mà tôi lấy nguồn ở đầu mỗi tập lệnh. Tuy nhiên, bạn cũng có thể sao chép nó trực tiếp khi bắt đầu mã của mình. Sau đó, chèn halt()vào bất kỳ điểm nào trong mã của bạn để ngừng thực thi tập lệnh ngay lập tức. Chức năng này hoạt động tốt trên GNU / Linux và đánh giá từ Rtài liệu, nó cũng sẽ hoạt động trên Windows (nhưng tôi đã không kiểm tra).

# 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) đóng phiên và loại bỏ ngay cả người dùng RStudio. Nó khá nguy hiểm nhưng có chức năng ....
Espanta

Tôi không biết nó sẽ làm hỏng RStudio, nhưng vấn đề tương tự được thảo luận trong: stackoverflow.com/questions/32820534/… Tuy nhiên, trên linux, giải pháp của tôi hoạt động tốt. Ưu điểm của nó so với stopifnot là thông báo Lỗi stopifnot () không hiển thị.
François Tonneau,

Tôi đã kiểm tra trên Windows và nó hoạt động rất điên rồ. Dẫu sao cũng xin cảm ơn. Tôi thích pskill.
Espanta

0

Đây:

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

Cả hai quit()stop(message)sẽ thoát khỏi tập lệnh của bạn. Nếu bạn đang tìm nguồn tập lệnh của mình từ dấu nhắc lệnh R, thì quit()bạn cũng sẽ thoát khỏi R.


7
Việc đăng các câu trả lời trùng lặp với những câu đã được đăng là một việc làm không tốt.
Thomas

@Thomas câu trả lời nào trùng lặp? Tôi chỉ thấy câu trả lời này sử dụng cả dừng và bỏ, và thực sự giải thích sự khác biệt giữa chúng.

@Thomas: Giải thích chính xác câu trả lời của tôi trùng lặp với câu trả lời nào.
stackoverflowuser2010

@Thomas: Tôi đã đặt một câu hỏi liên quan đến lời chỉ trích của bạn. Tôi đang chờ bạn vui lòng trả lời nó.
stackoverflowuser2010

5
Câu trả lời của @ netskink sử dụng stop() , và OP đã nêu trong ý kiến rằng họ không muốn quit()...
Bến Bolker
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.