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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Rのエラー、警告、メッセージ
- エラー回復のためのtryCatch()
- withCallingHandlers()と再起動
- stop()とwarning()による堅牢な関数の作成