Environments as Objects
Part of the Object Oriented Programming section of Coddy's R journey. Lesson 18 of 57.
An environment is a set of named values, like a list, created with new.env(). The difference is that an environment is never copied: every variable that refers to it sees the same object, so a change through one name is visible through all of them:
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)Output:
[1] 99
[1] 1A function that receives an environment changes the caller's object. It does not have to return anything, and the caller does not assign a result:
deposit <- function(acc, x) acc$balance <- acc$balance + x
acc <- new.env()
acc$balance <- 10
deposit(acc, 5)
deposit(acc, 5)
print(acc$balance)Output:
[1] 20assign(), get() and exists() work with a name held in a variable, and ls() lists the names in an environment in sorted order:
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))Output:
[1] "apple" "pear"
[1] 4
[1] FALSEAn environment with a class attribute is an S3 object with reference semantics: its methods work as usual, and functions that change it do so in place:
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)
wOutput:
wallet with 7 coinsChallenge
EasyAn inventory is an environment of class inventory with one variable, items, a named numeric vector of stock. Complete Inventory.R:
new_inventory()creates the environment with an emptyitems(c()is fine) and the class.add_stock(inv, item, n)addsnto the item's stock, starting from 0 for a new item.take_stock(inv, item, n)removesnand returnsTRUEwhen there is enough, otherwise changes nothing and returnsFALSE.stock_of(inv, item)returns the stock, 0 for an unknown item.
The functions change the inventory in place and do not return it. The supplied code reads commands add apple 5 and take apple 2, prints the result of each take, then every item in alphabetical order.
Your code goes in Inventory.R. main.R holds the supplied input/output code and cannot be edited.
Try it yourself
source("Inventory.R")
# Supplied input/output code: keep it as it is
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 = "")
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1S3 Basics
Working With FilesLists With a ClassConstructor FunctionsPrint and Format MethodsValidators and HelpersRecap - Temperatures4Encapsulation in R
Closures as ObjectsEnvironments as ObjectsAccessor FunctionsGuarding StateRecap - Parking Meter2S3 Generics and Methods
Generics and UseMethodDefault MethodsMethods for Base GenericsNextMethod BasicsRecap - Shape Areas8Reference Classes
Defining Reference ClassesMethods and Field UpdatesCopy SemanticsInheritance and callSuperRecap - Task Queue11Project: Library Management
Books and MembersBorrowing BooksPractice on your own: Online R compiler