Menu
Coddy logo textTech

オブジェクトとしての環境

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

環境は、new.env()で作成される、リストのような名前付きの値の集合です。違いは、環境が決してコピーされないことです。環境を参照するすべての変数は同じオブジェクトを見ているため、いずれかの名前を通じて行った変更は、それらすべてを通じて確認できます。

e <- new.env()
e$count <- 1
alias <- e
alias$count <- 99
print(e$count)
l <- list(count = 1)
copy <- l
copy$count <- 99
print(l$count)

出力:

[1] 99
[1] 1

環境を受け取る関数は、呼び出し元のオブジェクトを変更します。何も返す必要はなく、呼び出し元も結果を代入しません:

deposit <- function(acc, x) acc$balance <- acc$balance + x
acc <- new.env()
acc$balance <- 10
deposit(acc, 5)
deposit(acc, 5)
print(acc$balance)

出力:

[1] 20

assign()、get()、exists()は変数に格納された名前を扱い、ls()は環境内の名前を並べ替えられた順序で一覧表示します。

e <- new.env()
for (fruit in c("pear", "apple")) assign(fruit, nchar(fruit), envir = e)
print(ls(e))
print(get("pear", envir = e))
print(exists("kiwi", envir = e))

出力:

[1] "apple" "pear" 
[1] 4
[1] FALSE

class属性を持つ環境は、参照セマンティクスを持つS3オブジェクトです。そのメソッドは通常どおり動作し、それを変更する関数はその場で変更します。

new_wallet <- function(coins = 0) {
  w <- new.env()
  w$coins <- coins
  class(w) <- "wallet"
  w
}
print.wallet <- function(x, ...) {
  cat("wallet with", x$coins, "coins\n")
  invisible(x)
}
spend <- function(w, n) w$coins <- w$coins - n
w <- new_wallet(10)
spend(w, 3)
w

出力:

wallet with 7 coins
challenge icon

チャレンジ

簡単

在庫は、在庫を表す名前付き数値ベクトルである変数 items を1つ持つ、クラス inventory の環境です。Inventory.R を完成させてください:

  • new_inventory() は、空の items(c() で構いません)とクラスを持つ環境を作成します。
  • add_stock(inv, item, n) は、商品の在庫に n を追加します。新しい商品については、0から開始します。
  • take_stock(inv, item, n) は、在庫から n を減らし、十分な在庫がある場合は TRUE を返します。それ以外の場合は何も変更せず、FALSE を返します。
  • stock_of(inv, item) は在庫数を返します。不明な商品については0を返します。

関数は在庫をその場で変更し、在庫自体は返しません。提供されているコードは、コマンド add apple 5 と take apple 2 を読み取り、各 take の結果を出力してから、すべての商品をアルファベット順に出力します。

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

自分で試してみよう

source("Inventory.R")

# 提供された入出力コード: そのままにしておく
input <- suppressWarnings(readLines(file("stdin")))
inv <- new_inventory()
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  n <- as.numeric(p[3])
  if (p[1] == "add") add_stock(inv, p[2], n)
  if (p[1] == "take") cat("take ", p[2], ": ", if (take_stock(inv, p[2], n)) "ok" else "not enough", "\n", sep = "")
}
for (item in sort(names(inv$items))) cat(item, ": ", stock_of(inv, item), "\n", sep = "")
quiz icon腕試し

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

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

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