Code Fix

上級

tryCatch の error ハンドラは warning を捕まえない

tryCatch(expr, error = function(e) ...)のerrorハンドラは、条件クラスがerrorであるものしか捕まえません。warning()で発生させた警告は、実行が止まらないままerrorハンドラをすり抜けます。

なぜエラーが出ないのか

出力: result: NaN
(エラーなし)
Rは何も報告しません。文法としても実行としても正しいコードだからです
result: NaN
実際の出力 — 期待した結果と食い違っている箇所

このエラーが出る典型パターン

パターン1

 1  safe_sqrt <- function(x) {
 2    tryCatch({
 3      if (x < 0) warning("negative input")
 4      sqrt(x)
 5    }, error = function(e) {
 6      NA
 7    }                          )
                    ^
 8  }
 9  result <- safe_sqrt(-4)
10  cat("result:", result, "\n")
出力: result: NaN

errorハンドラだけを用意してもwarning()による警告は捕まえられず、sqrt(x)がそのまま実行されてNaNになります。捕まえたい場合はwarning=という別のハンドラが必要です。

直し方: (空) を , warning = function(w) NA にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  safe_log <- function(x) {
 2    tryCatch({
 3      if (x <= 0) warning("non-positive input")
 4      log(x)
 5    }, error = function(e) {
 6      NA
 7    }                          )
                    ^
 8  }
 9  result <- safe_log(-1)
10  cat("result:", result, "\n")
出力: result: NaN

warning()で発生させた警告はerrorハンドラをすり抜けてしまい、log(x)の計算が続行されNaNが返ります。

直し方: (空) を , warning = function(w) NA にします。

この問題を解いてみる →

パターン3

 1  parse_num <- function(s) {
 2    tryCatch({
 3      n <- as.numeric(s)
 4      n
 5    }, error = function(e) {
 6      -1
 7    }                          )
                    ^
 8  }
 9  result <- parse_num("abc")
10  cat("result:", result, "\n")
出力: result: NA

as.numeric("abc")は変換できない警告を出しつつNAを返しますが、この警告はerrorハンドラでは捕まえられません。

直し方: (空) を , warning = function(w) -1 にします。

この問題を解いてみる →

パターン4

 1  safe_div_check <- function(a, b) {
 2    tryCatch({
 3      if (b == 0) warning("division by zero")
 4      a / b
 5    }, error = function(e) {
 6      NA
 7    }                          )
                    ^
 8  }
 9  result <- safe_div_check(10, 0)
10  cat("result:", result, "\n")
出力: result: Inf

0除算はwarning()で警告を出すだけで実行は止まらず、errorハンドラには捕まらないままInfという結果になります。

直し方: (空) を , warning = function(w) NA にします。

この問題を解いてみる →

パターン5

 1  parse_int <- function(s) {
 2    tryCatch({
 3      n <- as.integer(s)
 4      n
 5    }, error = function(e) {
 6      -1
 7    }                         )
                   ^
 8  }
 9  result <- parse_int("xyz")
10  cat("result:", result, "\n")
出力: result: NA

as.integer("xyz")が出す「NAs introduced by coercion」という警告はerrorハンドラでは捕まえられず、そのままNAが結果になります。

直し方: (空) を , warning = function(w) 0 にします。

この問題を解いてみる →

よくある誤解

「tryCatchのerrorハンドラは、異常っぽいものは何でも捕まえてくれるはず」という思い込みは誤りです。R条件システムはerrorとwarningを別の条件クラスとして扱うため、警告も捕まえたい場合はwarning=という別のハンドラを明示的に用意する必要があります。

実務での勘所

Rの条件システムは、conditionという頂点の下にerror・warning・messageが兄弟同士として並ぶ、独立したクラス階層になっています。JavaやRubyの例外階層のように、警告が例外の一種として派生しているわけではありません。error用のハンドラがwarningを拾えないのは、片方がもう片方に含まれる関係ではなく、そもそも別系統の条件だからです。あらゆる種類の条件を1つのハンドラで受け止めたい場合は、tryCatch(expr, condition = function(c) ...)のように、階層の頂点にあるcondition自体を指定する必要があります。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)