[R]R語言里的異常處理與錯誤控制


之前一直只是在寫小程序腳本工具,幾乎不會對異常和錯誤進行控制和處理。

隨着腳本結構和邏輯更復雜,腳本輸出結果的准確性驗證困難,同時已發布腳本的維護也變得困難。所以也開始考慮引入異常處理和測試工具的事情。

不過好像R語言的異常處理似乎有些辣雞?查了下資料和try的文檔說明,感覺說的並不清楚。

在網上查了一些資料,對R語言異常處理做了比較詳細的說明,留檔作為參考。至於測試工具的問題,后續還是再考慮下。

文章鏈接:R語言-處理異常值或報錯的三個示例

原文參考了以下幾個網頁:

http://stackoverflow.com/questions/14059657/how-to-skip-an-error-in-a-loop

http://stackoverflow.com/questions/8093914/skip-to-next-value-of-loop-upon-error-in-r-trycatch (這個鏈接里還連接了非常多的關於R語言異常處理)。

============================= 本人的分割線 ===============================

在開始之前,或許應該參考一下其他語言里面的異常處理機制。

先奉上Java方面的參考資料:深入理解java異常處理機制

 

除了第4點,其他基本無參考意義。因為R已經將比較底層的東西給隱藏起來,異常(運行時異常、編譯異常等)方面的資料也缺少(cran文檔庫也沒有很具體的信息)。所以這里只參考第四點,討論處理異常機制。

R語言處理異常機制:

異常的處理分為拋出異常捕捉異常

拋出異常:當一個方法出現錯誤引發異常時,方法創建異常對象並交付運行時系統,異常對象中包含了異常類型和異常出現時的程序狀態等異常信息。

雖然不確定R中是否存在運行時系統這個概念,但是其他地方的描述,應該是跟Java的異常是一致的。

捕捉異常:

 

如果tryCatch中調用的語句產生一個錯誤,它會執行一個以error為參數的函數,並返回這個函數的值。以error為參數的函數,需要將錯誤本身作為一個參數(這里稱之為e),但是我們可以不對它做任何操作。

當然,我們也可以使用較為簡單的try語句。選擇哪個基於各自的喜好。

假如你的語句產生一個錯誤,try會返回一個類型為try-error的對象,並將其信息輸出至屏幕(若silent=FALSE)。

 

===== 0524 更 ========

http://stackoverflow.com/questions/8093914/skip-to-next-value-of-loop-upon-error-in-r-trycatch

使用tryCatch需要知道很關鍵的一點: tryCatch會返回一個對象。見如下代碼:

x <- tryCatch(stop("Error"), error = function(e) e)
class(x)
"simpleError" "error"       "condition"  

如果在tryCatch中存在一個error,則這個返回的對象會繼承error類。我們可以通過inherit函數來檢驗類繼承。

另: 在tryCatch中的參數 error = function(e) e 是什么意思呢?

這有點難到我了。文檔中也沒有看到很好的解釋。這當中的過程是,這個參數會捕獲你在tryCatch中的代碼表達式產生的任何error信息。假如捕獲到error,它則將返回的值作為tryCatch的值(記住tryCatch會返回一個對象)。文檔中將其稱為calling handler。在 error = function(e)中的e, 就是你代碼表達式中的錯誤信息。

關於在循環代碼中使用next:

程序性編程中認為使用next不太好。所以假如想要在for循環中去掉next,可以考慮:

 

for (i in 1:39487) {
  #ERROR HANDLING
  possibleError <- tryCatch(
      thing(),
      error=function(e) e
  )

  if(!inherits(possibleError, "error")){
    #REAL WORK
    useful(i); fun(i); good(i);
  }

}  #end for

 ======= 果然不只是我對tryCatch的文檔雲里霧里.... ================

參見:Using R — Basic error Handing with tryCatch()

在開始之前,我們先了解下異常處理中的相關函數列表。以下是這方面函數的最精簡的列表了:

  •  warning(...) - 生成warnings
  • stop(...) - 生成errors
  • suppressWarnings(expr) - 運行expr表達式, 並忽略任何warnings
  • tryCatch(...) - 運行代碼並賦值給異常處理器(exception handlers)

還有其他與異常處理相關的函數,不過以上這些已經足夠我們了解R的異常處理了。

R 異常處理與 JAVA中的try-catch-finally機制不同

如果你注意到的話,R在很多問題的處理上和其他大多數語言都不一樣。

Java、Python和C以及其他在此維基頁面(Exception handling syntax)上提到的語言,都使用語言語句(即關鍵字)來實現try-catch-finally。但R就特立獨行,它使用一個函數來實現....

不過如果你代碼寫的好的話,tryCatch()函數其實看起來和其他語言的try-catch語法差不多。上代碼:

result = tryCatch({
    expr
}, warning = function(w) {
    warning-handler-code
}, error = function(e) {
    error-handler-code
}, finally = {
    cleanup-code
}

在tryCatch()中,可以處理兩種情況:'warnings' 和 'errors'.要理解每一塊代碼的意義,關鍵是要清楚代碼的運行狀態與作用域。以下節選自?tryCatch文檔:

If a condition is signaled while evaluating ‘expr’ then [...] control is transferred to the ‘tryCatch’ call 
that established the handler[...] and the result returned by the handler is returned as the value of the ‘tryCatch’ call. [...]
The ‘finally’ expression is then evaluated in the context in which ‘tryCatch’ was called.

'expr'一次執行一行,直到遇到'condition',然后程序執行會完整地轉移給handler。

代碼能比文字表達更多信息。文末的獨立R腳本展現了一個健壯的異常處理系統的各種特性:

  • 在一個函數中產生warnings和errors
  • 在tryCatch()中設置warning和error處理器(handler)
  • 當函數產生warning或error時,提供備選的返回值
  • 修改warning和error信息的內容
  • 抑制warning信息

復制粘貼文末的腳本,使其可執行,並使用以下命令執行:

$ chmod +x tryCatch.Rscript
$ ./tryCatch.r 1
$ ./tryCatch.r 0
$ ./tryCatch.r a
$ ./tryCatch.r
$ ./tryCatch.r warning
$ ./tryCatch.r error
$ ./tryCatch.r suppress-warnings

注意當'suppress-warnings'時發生了什么。

以下是一些你需要知道的,關於這個實驗腳本的信息:

  1. tryCatch()的使用並不難(一旦你知道如何使用)
  2. condition handler 能訪問到warning()和stop()信息
  3. 在傳遞函數參數之前,准備好合適的類型轉換。
  4. 最理想的情況是,tryCatch()的代碼表達式只是一個單獨的函數。

以下是tryCatch.Rscript范例腳本。Hope You enjoy Error handling.

#!/usr/bin/env Rscript
# tryCatch.Rscript -- experiments with tryCatch

# Get any arguments
arguments <- commandArgs(trailingOnly=TRUE)
a <- arguments[1]

# Define a division function that can issue warnings and errors
myDivide <- function(d, a) {
  if (a == 'warning') {
    return_value <- 'myDivide warning result'
    warning("myDivide warning message")
  } else if (a == 'error') {
    return_value <- 'myDivide error result'
    stop("myDivide error message")
  } else {
    return_value = d / as.numeric(a)
  }
  return(return_value)
}

# Evalute the desired series of expressions inside of tryCatch
result <- tryCatch({

  b <- 2
  c <- b^2
  d <- c+2
  if (a == 'suppress-warnings') {
    e <- suppressWarnings(myDivide(d,a))
  } else {
    e <- myDivide(d,a) # 6/a
  }
  f <- e + 100

}, warning = function(war) {

  # warning handler picks up where error was generated
  print(paste("MY_WARNING:  ",war))
  b <- "changing 'b' inside the warning handler has no effect"
  e <- myDivide(d,0.1) # =60
  f <- e + 100
  return(f)

}, error = function(err) {

  # error handler picks up where error was generated
  print(paste("MY_ERROR:  ",err))
  b <- "changing 'b' inside the error handler has no effect"
  e <- myDivide(d,0.01) # =600
  f <- e + 100
  return(f)

}, finally = {

  print(paste("a =",a))
  print(paste("b =",b))
  print(paste("c =",c))
  print(paste("d =",d))
  # NOTE:  Finally is evaluated in the context of of the inital
  # NOTE:  tryCatch block and 'e' will not exist if a warning
  # NOTE:  or error occurred.
  #print(paste("e =",e))

}) # END tryCatch

print(paste("result =",result))

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM