コンストラクタ関数
CoddyのRジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 3/57。
R は S3 オブジェクトのフィールドを決して検査しないため、あるクラスのすべてのオブジェクトは、コンストラクターである1つの関数によって構築されるべきです。慣例として、低レベルのコンストラクターは new_ にクラス名を続けた名前で呼びます。各フィールドを引数として受け取り、stopifnot() で型を検査して、オブジェクトを返します。
new_money <- function(amount = double(), currency = character()) {
stopifnot(is.double(amount), is.character(currency))
structure(list(amount = amount, currency = currency), class = "money")
}
m <- new_money(9.5, "EUR")
cat(m$amount, m$currency)出力:
9.5 EURstopifnot() は、条件のいずれかが TRUE でない場合にプログラムを停止し、メッセージには失敗したチェックの名前が示されます。誤った型は、後で別の関数がオブジェクトを使用するときではなく、オブジェクトの構築時点で検出されます。
new_money <- function(amount = double(), currency = character()) {
stopifnot(is.double(amount), is.character(currency))
structure(list(amount = amount, currency = currency), class = "money")
}
m <- new_money("9.5", "EUR")出力:
Error in new_money("9.5", "EUR") : is.double(amount) is not TRUE
Calls: new_money -> stopifnot
Execution haltedコンストラクタはクラスに対して structure() を呼び出す唯一の場所なので、すべてのオブジェクトは同じ順序で同じフィールドを持ちます。多くのオブジェクトが必要なコードでは、ループ内または lapply() を使って呼び出します。
new_point <- function(x = 0, y = 0) {
stopifnot(is.numeric(x), is.numeric(y))
structure(list(x = x, y = y), class = "point")
}
pts <- lapply(1:3, function(i) new_point(i, i * i))
for (p in pts) cat(p$x, p$y, "\n")出力:
1 1
2 4
3 9 S3オブジェクトはリストである必要はありません。コンストラクタはプレーンベクトルにラベルを付け、属性として追加の情報を保存できます。これは、R自身の因子や日付が機能する仕組みです。attr(x, "unit")は属性を読み取ります:
new_distance <- function(value = double(), unit = "m") {
stopifnot(is.double(value), is.character(unit))
structure(value, unit = unit, class = "distance")
}
d <- new_distance(c(1.5, 2), "km")
print(attr(d, "unit"))
print(unclass(d)[2] * 1000)出力:
[1] "km"
[1] 2000チャレンジ
簡単Book.R のコンストラクター new_book(title, author, pages) を完成させてください。stopifnot() を使って、title と author が文字型であり、pages が数値型であることを確認し、これら3つのフィールドとクラス book を持つリストを返します。次に、Dune by Frank Herbert, 412 pages を返す book_label(b) を完成させてください。
提供されたコードは、行 title;author;pages を読み取ります。pages が数字だけで構成されている場合は数値として、それ以外の場合は元のテキストとして渡し、各オブジェクトについて book: とラベルを出力します。また、コンストラクターが停止した場合は、rejected: とタイトルを出力します。
コードは Book.R に記述してください。main.R には提供された入力/出力コードが含まれており、編集できません。
自分で試してみよう
source("Book.R")
# 提供された入出力コード: そのままにしておく
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
p <- strsplit(line, ";")[[1]]
pages <- if (grepl("^[0-9]+$", p[3])) as.numeric(p[3]) else p[3]
b <- tryCatch(new_book(p[1], p[2], pages), error = function(e) NULL)
if (is.null(b)) {
cat("rejected: ", p[1], "\n", sep = "")
} else {
cat(class(b), ": ", book_label(b), "\n", sep = "")
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rオンラインコンパイラ