Menu
Coddy logo textTech

コピーのセマンティクス

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

S3 オブジェクトと S4 オブジェクトは値です。ある関数が一方を変更すると、その関数自身のコピーが変更されます。RC オブジェクトは参照です。呼び出し元のオブジェクトが変更されます。同じ関数でも、この 2 種類では動作が異なります。

setClass("S4Box", slots = c(n = "numeric"))
RCBox <- setRefClass("RCBox", fields = list(n = "numeric"))
bump_s4 <- function(b) { b@n <- b@n + 1; invisible(b) }
bump_rc <- function(b) b$n <- b$n + 1
v <- new("S4Box", n = 1)
r <- RCBox$new(n = 1)
bump_s4(v)
bump_rc(r)
cat(v@n, r$n)

出力:

1 2

$copy()は、同じフィールド値を持つ新しい独立したオブジェクトを作成します。コピーを変更しても、元のオブジェクトには影響しません。

Account <- setRefClass("Account", fields = list(balance = "numeric"))
a <- Account$new(balance = 100)
alias <- a
backup <- a$copy()
alias$balance <- 0
cat(a$balance, backup$balance)

出力:

0 100

リストには同じRCオブジェクトを複数回格納できます。1つの要素を変更すると、それらすべてが変更されます。各要素を独立させる必要がある場合は、要素ごとに新しいオブジェクトを作成します。

Counter <- setRefClass("Counter", fields = list(n = "numeric"))
one <- Counter$new(n = 0)
same <- list(one, one, one)
same[[1]]$n <- 5
fresh <- lapply(1:3, function(i) Counter$new(n = 0))
fresh[[1]]$n <- 5
cat(same[[3]]$n, fresh[[3]]$n)

出力:

5 0

identical(a, b) は、同じ RC オブジェクトを指す 2 つの名前に対しては TRUE となり、フィールドがまだ等しい場合でも、コピーに対しては FALSE となります:

Account <- setRefClass("Account", fields = list(balance = "numeric"))
a <- Account$new(balance = 100)
b <- a
c2 <- a$copy()
cat(identical(a, b), identical(a, c2), c2$balance == a$balance)

出力:

TRUE FALSE TRUE
challenge icon

チャレンジ

簡単

テキストエディタは保存ポイントを保持します。Document.R で、フィールド text(character)と、s をテキストに追加するメソッド type(s) を持つ Reference Class Document を宣言してください。次に、ドキュメントの独立したコピーを返す checkpoint(doc) を記述してください。

メソッドを含む定義は suppressMessages() でラップされています。提供されたコードは最初に保存ポイントを作成し、その後コマンドを読み取ります。save は checkpoint(doc) を保存し、undo は最後の保存ポイントのコピーに戻り、それ以外の行は入力されます。各コマンドの後にテキストと保存されたテキストを出力するため、入力を続けても保存ポイントが決して変更されないようにする必要があります。

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

自分で試してみよう

source("Document.R")

# 提供された入出力コード: そのままにしておく
input <- suppressWarnings(readLines(file("stdin")))
doc <- Document$new(text = "")
saved <- checkpoint(doc)
for (cmd in input) {
  if (cmd == "save") {
    saved <- checkpoint(doc)
  } else if (cmd == "undo") {
    doc <- checkpoint(saved)
  } else {
    doc$type(cmd)
  }
  cat(cmd, " -> [", doc$text, "] saved [", saved$text, "]\n", sep = "")
}
quiz icon腕試し

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

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

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