Menu
Coddy logo textTech

アクセサ関数

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

カプセル化とは、他のコードがオブジェクトのフィールドに直接アクセスするのではなく、関数を介して操作することです。getterは値を読み取り、setterは値を変更します。S3リストの場合、setterは変更後のコピーを返します。

new_member <- function(name) structure(list(name = name), class = "member")
get_name <- function(p) p$name
set_name <- function(p, value) {
  p$name <- value
  p
}
p <- new_member("ada")
p <- set_name(p, "Ada Lovelace")
print(get_name(p))

出力:

[1] "Ada Lovelace"

アクセサーを使うと、それを利用するコードを壊さずに、オブジェクトがデータを格納する方法を変更できます。この温度計は摂氏で値を格納し、求められたときにゲッターが華氏を計算するため、呼び出し元からはフィールド自体が見えません。

new_thermo <- function(c) structure(list(c = c), class = "thermo")
celsius <- function(t) t$c
fahrenheit <- function(t) t$c * 9 / 5 + 32
t <- new_thermo(25)
cat(celsius(t), fahrenheit(t))

出力:

25 77

R 独自のセッター(names(x) <- ... など)は、置換関数です。x と value をパラメーターとして持つ "name<-" という名前の関数を定義すると、呼び出し側は name(p) <- "Bo" と書けます。R はその関数を呼び出し、結果を p に再代入します。

new_member <- function(name) structure(list(name = name), class = "member")
name <- function(p) p$name
"name<-" <- function(x, value) {
  x$name <- trimws(value)
  x
}
p <- new_member("ada")
name(p) <- "  Bo  "
print(name(p))

出力:

[1] "Bo"

クロージャオブジェクトのゲッターは、プライベートデータのコピーを返します。そのコピーを変更してもオブジェクトは変わらないため、オブジェクトにアクセスする唯一の方法は、オブジェクト自身の関数を介することです:

make_list <- function() {
  items <- character(0)
  list(add = function(x) items <<- c(items, x),
       get_items = function() items)
}
todo <- make_list()
todo$add("milk")
mine <- todo$get_items()
mine[1] <- "HACKED"
print(todo$get_items())

出力:

[1] "milk"
challenge icon

チャレンジ

簡単

連絡先は、name フィールドと email フィールドを持つクラス contact の S3 リストです。Contact.R を完成させてください。

  • new_contact(name, email) は、メールアドレスを前後の空白を除去し、小文字に変換して保存します。
  • ゲッター contact_name(x) と contact_email(x)。
  • 置換関数 "contact_email<-" は、新しいメールアドレスを前後の空白を除去し、小文字に変換して保存するため、呼び出し側では contact_email(c1) <- value と記述できます。

用意されたコードは name,email を読み取り、続いて新しいメールアドレスを読み取り、変更のたびに Ada <ada@x.com> を出力し、最後に保存されているフィールド名を出力します。

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

自分で試してみよう

source("Contact.R")

# 提供された入出力コード: そのままにしておく
input <- suppressWarnings(readLines(file("stdin")))
p <- strsplit(input[1], ",")[[1]]
c1 <- new_contact(p[1], p[2])
cat(contact_name(c1), " <", contact_email(c1), ">\n", sep = "")
for (value in input[-1]) {
  contact_email(c1) <- value
  cat(contact_name(c1), " <", contact_email(c1), ">\n", sep = "")
}
cat("stored fields: ", paste(names(unclass(c1)), collapse = ", "), "\n", sep = "")
quiz icon腕試し

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

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

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