0Pricing
R Academy · レッスン

stop()とwarning()による堅牢な関数の作成

独自の関数内からカスタムコンディションを通知します。

「stop()とwarning()による堅牢な関数の作成」はCoddyKit上の無料R Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはR Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 R Academyコースには全4レッスンが含まれています。

入力検証が重要な理由

防御的な関数は入力を検査し、誤った結果を黙って生成するのではなく、情報量の多いエラーを明示的に発生させます。境界で不正な入力を検出する関数は、パイプライン全体に不正なデータを流してしまう関数より、はるかにデバッグしやすくなります。

# Without validation: wrong result, no error
bad_mean <- function(x) sum(x) / length(x)
bad_mean('hello')   # no error, just NA

# With validation: clear error
good_mean <- function(x) {
  if (!is.numeric(x)) stop('x must be numeric')
  sum(x) / length(x)
}
tryCatch(good_mean('hello'), error = function(e) cat(e$message, '\n'))

情報量の多いエラーメッセージ

stop('bad input')のような一般的なエラーはデバッグを困難にします。stop()の中でpaste0()またはsprintf()を使用し、実際の値と期待される型をメッセージに含めてください。これにより、パイプライン内でエラーを追跡する時間を短縮できます。

check_numeric <- function(x, arg_name = 'x') {
  if (!is.numeric(x)) {
    stop(sprintf(
      "'%s' must be numeric, but got class '%s'",
      arg_name, class(x)
    ))
  }
}
tryCatch(check_numeric('abc', 'score'),
  error = function(e) cat(e$message, '\n'))

長さと範囲の検証

よく行う検証には、ベクトルが空でないこと(length(x) == 0)、スカラーが範囲内にあること、データフレームに期待する列があることの確認などがあります。それぞれのチェックには、目的に合わせたエラーメッセージを用意してください。

validate_prob <- function(p) {
  if (!is.numeric(p)) stop(paste0('p must be numeric, got: ', class(p)))
  if (length(p) != 1) stop(paste0('p must be length 1, got: ', length(p)))
  if (p < 0 || p > 1) stop(paste0('p must be in [0,1], got: ', p))
  p
}
cat(validate_prob(0.7), '\n')
tryCatch(validate_prob(1.5), error = function(e) cat(e$message, '\n'))

致命的でない問題にwarning()を使う

値の型変換、デフォルト値の補完、境界付近の入力の検出など、関数が妥当な結果を返せるものの、通常と異なることが起きたと呼び出し元に知らせるべき場合は、warning()を使用します。

clamp <- function(x, lo, hi) {
  if (any(x < lo)) warning(paste0(sum(x < lo), ' value(s) below lo, clamped'))
  if (any(x > hi)) warning(paste0(sum(x > hi), ' value(s) above hi, clamped'))
  pmax(lo, pmin(hi, x))
}
result <- clamp(c(2, -5, 7, 100, 4), 0, 10)
cat('Result:', result, '\n')

stop()のcall. = FALSE

デフォルトでは、stop()はメッセージの先頭にError in funcname(...):を付けます。呼び出し元のコンテキストが明らかな場合や、ユーザー向けユーティリティの場合は、call. = FALSEを渡してこの接頭辞を抑制します。

# With call. = TRUE (default): shows calling function
f1 <- function(x) stop('bad input')
tryCatch(f1(1), error = function(e) cat(conditionMessage(e), '\n'))

# With call. = FALSE: cleaner message
f2 <- function(x) stop('bad input', call. = FALSE)
tryCatch(f2(1), error = function(e) cat(conditionMessage(e), '\n'))

stop()用のカスタム条件クラス

structure()を使用してカスタムエラークラスを作成すると、すべてのエラーを捕捉せず、呼び出し元が自分の関数のエラーだけを捕捉できるようになります。これは、パッケージレベルのエラーを記述するための、専門的な方法です。

value_error <- function(msg, call = sys.call(-1)) {
  structure(
    class = c('value_error', 'error', 'condition'),
    list(message = msg, call = call)
  )
}
check_positive <- function(x) {
  if (x <= 0) stop(value_error(paste0('Expected positive, got: ', x)))
  x
}
tryCatch(check_positive(-3),
  value_error = function(e) cat('ValueError:', e$message, '\n'),
  error        = function(e) cat('Other error:', e$message, '\n'))

データフレームの列を確認する

関数がデータフレームを受け取る場合は、使用する前に必要な列が存在することを検証してください。%in%で列名を確認し、setdiff()で不足している列を報告します。

require_cols <- function(df, cols) {
  missing <- setdiff(cols, names(df))
  if (length(missing) > 0) {
    stop(paste0('Missing columns: ', paste(missing, collapse = ', ')))
  }
  invisible(df)
}
df <- data.frame(x = 1:3, y = 4:6)
tryCatch(
  require_cols(df, c('x', 'z', 'w')),
  error = function(e) cat(e$message, '\n')
)

簡潔なアサーションにstopifnot()を使う

stopifnot(condition1, condition2, ...)は、いずれかの条件がFALSEの場合にエラーを発生させます。関数の先頭で事前条件を確認する際に、個別のif (!...) stop(...)ブロックを書かずに済む簡潔な方法です。

compute_area <- function(width, height) {
  stopifnot(
    is.numeric(width),
    is.numeric(height),
    width > 0,
    height > 0
  )
  width * height
}
cat(compute_area(5, 3), '\n')
tryCatch(compute_area(-1, 3), error = function(e) cat(e$message, '\n'))

より良いメッセージのための名前付きstopifnot()

R 4.0以降では、stopifnot()内の名前付き式によって、自動生成されるメッセージをカスタム説明に置き換えられます。式のテキストを名前として使用すると、失敗の内容を分かりやすく示せます。

validate_age <- function(age) {
  stopifnot(
    'age must be numeric'    = is.numeric(age),
    'age must be positive'   = age > 0,
    'age must be under 150'  = age < 150
  )
  invisible(age)
}
tryCatch(validate_age(-5), error = function(e) cat(e$message, '\n'))
tryCatch(validate_age('x'), error = function(e) cat(e$message, '\n'))

検証とビジネスロジックの組み合わせ

適切に構成された関数では、検証とロジックを分離します。最初に検証を行い、その後で処理を実行します。これにより関数が読みやすくなり、計算を開始する前にエラーを検出できます。

discount_price <- function(price, pct) {
  if (!is.numeric(price) || price <= 0)
    stop(paste0('price must be positive numeric, got: ', price))
  if (!is.numeric(pct) || pct < 0 || pct > 100)
    stop(paste0('pct must be in [0,100], got: ', pct))
  if (pct > 50) warning('discount > 50% is unusual')
  price * (1 - pct / 100)
}
cat(discount_price(100, 20), '\n')
cat(suppressWarnings(discount_price(100, 60)), '\n')

ユーティリティ関数内でのtryCatch

ユーティリティ関数では、エラーを呼び出し元へ伝播させるのではなく、内部で処理してフォールバックを返すべき場合があります。中心となるロジックをtryCatch()で囲み、失敗時にはNAやNULLなど、あらかじめ決めたセンチネル値を返します。

safe_log <- function(x) {
  tryCatch({
    if (!is.numeric(x)) stop('not numeric')
    if (x <= 0) stop('must be positive')
    log(x)
  }, error = function(e) {
    warning(paste0('safe_log failed for x=', x, ': ', e$message))
    NA_real_
  })
}
results <- sapply(list(10, -1, 'a', 100), safe_log)
cat(results, '\n')

確認問題

stop()にcall. = FALSEを渡すと、どのような動作になりますか?

堅牢な関数: 重要なポイント

堅牢な関数を記述する際の重要なポイント:

  • 最初に入力を検証し、情報量の多いstop()メッセージで明示的に失敗させます
  • エラーメッセージに実際の値を含めます:paste0('Expected numeric, got: ', class(x))
  • 処理を継続できる致命的でない問題にはwarning()を使用します
  • call. = FALSEで、エラーメッセージ内の呼び出し元関数の接頭辞を抑制します
  • 簡潔なアサーションにはstopifnot()を使用し、カスタムメッセージには名前付き形式を使用します(R 4.0以降)
  • カスタム条件クラスにより、呼び出し元が選択的にエラーを捕捉できます
robust_divide <- function(x, y) {
  stopifnot('x must be numeric' = is.numeric(x),
            'y must be numeric' = is.numeric(y))
  if (y == 0) stop('Division by zero', call. = FALSE)
  if (abs(y) < 1e-10) warning('y is very small; result may be inaccurate')
  x / y
}
cat(robust_divide(10, 2), '\n')
tryCatch(robust_divide(10, 0), error = function(e) cat(e$message, '\n'))

よくある質問

「stop()とwarning()による堅牢な関数の作成」レッスンは無料ですか?

はい。「stop()とwarning()による堅牢な関数の作成」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、R Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 R Academyコースには全4レッスンが含まれています。

「stop()とwarning()による堅牢な関数の作成」で何を学びますか?

独自の関数内からカスタムコンディションを通知します。 ブラウザで直接実行するハンズオンコードでR Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

R Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのR Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「stop()とwarning()による堅牢な関数の作成」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このR Academyレッスンでコードを書いて実行できますか?

はい。すべてのR Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Rのエラー、警告、メッセージ
  2. エラー回復のためのtryCatch()
  3. withCallingHandlers()と再起動
  4. stop()とwarning()による堅牢な関数の作成
← R Academyに戻る