Closures with State
Part of the Logic & Flow section of Coddy's R journey. Lesson 30 of 64.
Inside a function, <- creates a local variable. The operator <<- assigns to the variable of the same name in the enclosing function instead, so a closure can update a value it captured:
make_counter <- function() {
count <- 0
function() {
count <<- count + 1
count
}
}
next_id <- make_counter()
print(next_id())
print(next_id())
print(next_id())Output:
[1] 1
[1] 2
[1] 3Each counter made by the maker has its own count, so they never interfere:
make_counter <- function() {
count <- 0
function() {
count <<- count + 1
count
}
}
a <- make_counter()
b <- make_counter()
for (i in 1:2) a()
print(a())
print(b())Output:
[1] 3
[1] 1With a plain <- inside the closure, the update creates a new local variable that disappears after each call, and the stored value never changes:
make_broken <- function() {
count <- 0
function() {
count <- count + 1
count
}
}
f <- make_broken()
for (i in 1:2) f()
print(f())Output:
[1] 1A maker can return a list of closures that share the same variables. Together they behave like a small object with its own data:
make_wallet <- function(balance) {
list(
add = function(x) balance <<- balance + x,
get = function() balance
)
}
w <- make_wallet(10)
w$add(5)
print(w$get())Output:
[1] 15Challenge
EasyComplete make_counter(). It returns a function that, every time it is called, adds 1 to its own count and returns the new count, starting from 1.
The supplied code makes two counters, a and b. It reads a comma-separated list of a and b, calls the matching counter for each entry and prints each returned value on its own line.
Try it yourself
make_counter <- function() {
# Write your code here
function() 0
}
# Supplied input/output code: keep it as it is
input <- suppressWarnings(readLines(file("stdin")))
counters <- list(a = make_counter(), b = make_counter())
for (k in strsplit(input[1], ",")[[1]]) cat(counters[[k]](), "\n", sep = "")
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Logic & Flow
1Strings In Depth
Substrings with substr()Formatting with sprintf()Splitting and JoiningSearching StringsReplacing TextRecap - Username Builder4Matrices
Creating MatricesIndexing MatricesRow and Column SummariesMatrix ArithmeticRecap - Seating Chart2Key-Value Lookups
Named Vector LookupsChecking KeysAdding and Removing KeysLooping Over NamesRecap - Stock Desk3Sets and Counting
Unique ValuesSet OperationsMembership TestsCounting with table()Recap - Event Guests6Functions as Values
Anonymous FunctionsPassing FunctionsReturning FunctionsClosures with StateRecap - Discount Rules9Data Frames
Creating Data FramesColumns and RowsFiltering RowsAdding and SortingRecap - Sales Report12Project - Expense Tracker
Recording ExpensesTotal SpendingPractice on your own: Online R compiler