Menu
Coddy logo textTech

バリデーターとヘルパー

CoddyのRジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 5/57。

コンストラクターは型をチェックし、バリデーターは値をチェックします。慣例として、validate_money()はオブジェクトを受け取り、違反している各ルールについて明確なメッセージとともにstop()を呼び出し、すべて問題なければオブジェクトを返します。call. = FALSEにより、関数呼び出しがメッセージに含まれなくなります。

new_money <- function(amount = double(), currency = character()) {
  stopifnot(is.double(amount), is.character(currency))
  structure(list(amount = amount, currency = currency), class = "money")
}
validate_money <- function(x) {
  if (x$amount < 0) stop("amount must not be negative", call. = FALSE)
  if (nchar(x$currency) != 3) stop("currency must have 3 letters", call. = FALSE)
  x
}
m <- validate_money(new_money(-5, "EUR"))

出力:

Error: amount must not be negative
Execution halted

ヘルパーは、他のコードから呼び出される関数です。クラスにちなんで名付けられ、入力を適切な型に変換し、デフォルト値を設定して、その結果をコンストラクターとバリデーターに渡します。

new_money <- function(amount = double(), currency = character()) {
  stopifnot(is.double(amount), is.character(currency))
  structure(list(amount = amount, currency = currency), class = "money")
}
validate_money <- function(x) {
  if (x$amount < 0) stop("amount must not be negative", call. = FALSE)
  x
}
money <- function(amount, currency = "EUR") {
  validate_money(new_money(as.double(amount), toupper(currency)))
}
m <- money("12", "usd")
cat(m$amount, m$currency)

出力:

12 USD

ユーザー入力を読み取るコードは、tryCatch() を使ってバリデーターのエラーを捕捉し、stop() に渡されたテキストである conditionMessage(e) を表示します:

validate_age <- function(x) {
  if (x$years < 0) stop("age must not be negative", call. = FALSE)
  if (x$years > 150) stop("age is too large", call. = FALSE)
  x
}
age <- function(years) validate_age(structure(list(years = as.double(years)), class = "age"))
for (input in c("30", "-2", "200")) {
  result <- tryCatch(paste("ok", age(input)$years), error = function(e) conditionMessage(e))
  cat(result, "\n")
}

出力:

ok 30 
age must not be negative 
age is too large 

3つの関数には、それぞれ1つの役割があります。new_money()は厳密かつ高速で、すでに正しい型になっているコード向けです。validate_money()は値に関するルールを保持しており、その処理に時間がかかったり、ルールが多かったりする場合があります。money()は使いやすく、プログラムの残りの部分で使用する関数です:

new_money()       # 型のみ;structure() でオブジェクトを構築する
validate_money()  # 値のルール;メッセージ付きで stop()、さもなければ x を返す
money()           # 入力を変換し、その後 validate_money(new_money(...))
challenge icon

チャレンジ

簡単

new_score(points, max) は Score.R で score オブジェクトを構築します。同じファイル内の他の関数を完成させてください。

  • validate_score(x) は、points must not be negative、max must be positive、または points must not exceed max で、指定された順序で検証し、いずれにも該当しない場合は x を返します。
  • score(points, max = 100) は両方の引数を as.double() で変換し、validate_score(new_score(...)) を返します。
  • format.score(x, ...) は 45/50 (90%) を返します。パーセンテージは整数に丸めてください。

すべての stop() で call. = FALSE を使用してください。提供されたコードは行 45 50 を読み取り、整形されたスコア、または error: とメッセージを出力します。

コードは Score.R に記述してください。main.R には提供された入出力コードが含まれており、編集できません。

自分で試してみよう

source("Score.R")

# 提供された入出力コード: そのままにしておく
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  s <- tryCatch(score(p[1], p[2]), error = function(e) conditionMessage(e))
  if (is.character(s)) cat("error: ", s, "\n", sep = "") else cat(format(s), "\n", sep = "")
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Rオンラインコンパイラ