オブジェクトとしてのクロージャ
CoddyのRジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 17/57。
Logic & Flow でクロージャについて学びました。ある関数の中で作られた関数は、その周囲にある変数を保持します。このような関数のリストを返す関数ファクトリは、オブジェクトを構築します。そのローカル変数はオブジェクトのデータであり、関数はそのメソッドです。
make_account <- function(balance = 0) {
deposit <- function(x) balance <<- balance + x
get_balance <- function() balance
list(deposit = deposit, get_balance = get_balance)
}
acc <- make_account(100)
acc$deposit(50)
print(acc$get_balance())出力:
[1] 150ファクトリを呼び出すたびにその本体が再度実行されるため、各オブジェクトはそれぞれ独自の変数を持ちます。一方のアカウントを変更しても、もう一方には影響しません。
make_account <- function(balance = 0) {
list(deposit = function(x) balance <<- balance + x,
get_balance = function() balance)
}
ada <- make_account(100)
bo <- make_account(5)
ada$deposit(1)
cat(ada$get_balance(), bo$get_balance())出力:
101 5データはプライベートです。balanceはリストの要素ではないため、acc$balanceはNULLとなり、そこへの代入は無関係な要素を追加するだけです。リスト内の関数が、そのオブジェクトの公開インターフェース全体です:
make_account <- function(balance = 0) {
list(get_balance = function() balance)
}
acc <- make_account(100)
print(acc$balance)
acc$balance <- 1e6
print(acc$get_balance())出力:
NULL
[1] 100メソッドは、リストが構築される前にローカル関数として定義されていれば、互いに呼び出すことができます。ここでは、add_many() が add() を再利用しています。
make_tally <- function() {
total <- 0
add <- function(x) total <<- total + x
add_many <- function(xs) for (x in xs) add(x)
get <- function() total
list(add = add, add_many = add_many, get = get)
}
t <- make_tally()
t$add(1)
t$add_many(c(2, 3, 4))
print(t$get())出力:
[1] 10チャレンジ
簡単make_counter(start = 0) ファクトリを Counter.R に完成させてください。現在の値を非公開に保ち、4つの関数を含むリストを返します。increment() と decrement() は値を1ずつ変更し、reset() は値を start に戻し、value() はその値を返します。
提供されたコードは開始値を読み取り、続いて +、-、または reset のコマンドを読み取り、それぞれの後に値を出力します。最後に、返されたリスト内の名前を出力します。このリストには4つの関数だけが含まれていなければなりません。
コードは Counter.R に記述してください。main.R には提供された入出力コードが含まれており、編集できません。
自分で試してみよう
source("Counter.R")
# 提供された入出力コード: そのままにしておく
input <- suppressWarnings(readLines(file("stdin")))
counter <- make_counter(as.numeric(input[1]))
for (cmd in input[-1]) {
if (cmd == "+") counter$increment()
if (cmd == "-") counter$decrement()
if (cmd == "reset") counter$reset()
cat(cmd, " -> ", counter$value(), "\n", sep = "")
}
cat("fields visible: ", paste(sort(names(counter)), collapse = ", "), "\n", sep = "")
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Rオンラインコンパイラ