title | author | date | output | ||||
---|---|---|---|---|---|---|---|
Chapter 14 |
Julin Maloof |
2023-04-07 |
|
R6
"in most cases R6 will lead you to non-idiomatic R code."
library(R6)
Use the R6Class()
function to define a class. Assign it to an object that has the same name as the class. First argument is class name, second argument is public fields and methods.
Accumulator <- R6Class("Accumulator", list(
sum = 0,
add = function(x = 1) {
self$sum <- self$sum + x
invisible(self)
})
)
Accumulator
## <Accumulator> object generator
## Public:
## sum: 0
## add: function (x = 1)
## clone: function (deep = FALSE)
## Parent env: <environment: R_GlobalEnv>
## Locked objects: TRUE
## Locked class: FALSE
## Portable: TRUE
Create a new object of this clas with $new function
x <- Accumulator$new()
x$sum
## [1] 0
x$add(4)
x$sum
## [1] 4
side-effect R6 methods (those that update a field and are not called to create an output) should return self invisibly
then you can do things like this
x$
add(10)$
add(10)$
sum
## [1] 24
Generally should define $initialize and $print methods to override the defaults
Person <- R6Class("Person", list(
name = NULL,
age = NA,
initialize = function(name, age = NA) {
self$name <- name
self$age <- age
},
print = function(...) {
cat("Person: \n")
cat(" Name: ", self$name, "\n", sep = "")
cat(" Age: ", self$age, "\n", sep = "")
invisible(self)
}
))
hadley2 <- Person$new("Hadley")
hadley2
## Person:
## Name: Hadley
## Age: NA
Use $set
Accumulator <- R6Class("Accumulator")
Accumulator$set("public", "sum", 0)
Accumulator$set("public", "add", function(x = 1) {
self$sum <- self$sum + x
invisible(self)
})
Will only impact newly created objects, will not be added to previosly created objects of the same class.
AccumulatorChatty <- R6Class("AccumulatorChatty",
inherit = Accumulator,
public = list(
add = function(x = 1) {
cat("Adding ", x, "\n", sep = "")
super$add(x = x)
}
)
)
x2 <- AccumulatorChatty$new()
x2$add(10)$add(1)$sum
## Adding 10
## Adding 1
## [1] 11
Every R6 object has an S3 class hierarchy
class(hadley2)
## [1] "Person" "R6"
1 Create a bank account R6 class that stores a balance and allows you to deposit and withdraw money. Create a subclass that throws an error if you attempt to go into overdraft. Create another subclass that allows you to go into overdraft, but charges you a fee.
BankAccountBasic <- R6Class("BankAcountSimple", list(
balance = 0,
deposit = function(amt) {
self$balance <- self$balance + amt
invisible(self)
},
withdraw = function(amt) {
self$balance <- self$balance - amt
invisible(self)
},
print = function() {
cat("The current balance is: ", self$balance, "\n")
invisible(self)
}
))
BA1 <- BankAccountBasic$new()
BA1$deposit(100)
BA1$withdraw(33)
BA1
## The current balance is: 67
BankAccount2 <- R6Class("BankAcount2",
inherit = BankAccountBasic,
public = list(
withdraw=function(amt) {
stopifnot(self$balance-amt>=0)
super$withdraw(amt)
}
))
BA2 <- BankAccount2$new()
BA2$deposit(100)
BA2$withdraw(33)
BA2
## The current balance is: 67
BA2$withdraw(100)
## Error in BA2$withdraw(100): self$balance - amt >= 0 is not TRUE
BA2
## The current balance is: 67
BankAccount3 <- R6Class("BankAcount3",
inherit = BankAccountBasic,
public = list(
withdraw=function(amt) {
super$withdraw(amt)
if (self$balance < 0) {
warning("New balance is less than $0, a $5.00 fee is being assessed\n")
super$withdraw(5)
}
}
))
BA3 <- BankAccount3$new()
BA3$deposit(100)
BA3$withdraw(150)
## Warning in BA3$withdraw(150): New balance is less than $0, a $5.00 fee is being assessed
BA3
## The current balance is: -55
2. Create an R6 class that represents a shuffled deck of cards. You should be able to draw cards from the deck with $draw(n), and return all cards to the deck and reshuffle with $reshuffle(). Use the following code to make a vector of cards.
deck <- R6Class("deck", public = list(
stack=character(),
drawn=character(),
initialize = function() {
suit <- c("♠", "♥", "♦", "♣")
value <- c("A", 2:10, "J", "Q", "K")
self$stack <- sample(paste0(rep(value, 4), suit))
self$drawn <- character()
},
reshuffle = function() {
self$stack <- c(self$stack, self$drawn)
self$drawn <- character()
self$stack <- sample(self$stack)
},
draw = function(n) {
newdraw <- self$stack[1:n]
self$drawn <- c(self$drawn, newdraw)
self$stack <- self$stack[-1:-n]
cat(newdraw)
},
print = function() {
cat("Not Drawn: ",self$stack, "\n")
cat("Drawn: ", self$drawn, "\n")
}
))
mydeck <- deck$new()
mydeck
## Not Drawn: A♥ 3♠ 10♣ 8♦ 2♣ 2♠ J♥ 6♣ 6♠ 9♦ 8♥ 3♣ 10♦ 7♠ 4♦ 3♥ 2♦ Q♥ K♥ 4♣ 8♠ 9♥ 2♥ A♠ 5♣ 7♥ 7♦ J♣ 10♠ Q♠ K♦ 7♣ 9♣ 8♣ 4♥ Q♦ K♣ J♦ J♠ 5♦ 9♠ 6♥ 6♦ Q♣ K♠ 4♠ A♣ A♦ 10♥ 5♥ 3♦ 5♠
## Drawn:
mydeck$draw(5)
## A♥ 3♠ 10♣ 8♦ 2♣
mydeck
## Not Drawn: 2♠ J♥ 6♣ 6♠ 9♦ 8♥ 3♣ 10♦ 7♠ 4♦ 3♥ 2♦ Q♥ K♥ 4♣ 8♠ 9♥ 2♥ A♠ 5♣ 7♥ 7♦ J♣ 10♠ Q♠ K♦ 7♣ 9♣ 8♣ 4♥ Q♦ K♣ J♦ J♠ 5♦ 9♠ 6♥ 6♦ Q♣ K♠ 4♠ A♣ A♦ 10♥ 5♥ 3♦ 5♠
## Drawn: A♥ 3♠ 10♣ 8♦ 2♣
mydeck$draw(5)
## 2♠ J♥ 6♣ 6♠ 9♦
mydeck
## Not Drawn: 8♥ 3♣ 10♦ 7♠ 4♦ 3♥ 2♦ Q♥ K♥ 4♣ 8♠ 9♥ 2♥ A♠ 5♣ 7♥ 7♦ J♣ 10♠ Q♠ K♦ 7♣ 9♣ 8♣ 4♥ Q♦ K♣ J♦ J♠ 5♦ 9♠ 6♥ 6♦ Q♣ K♠ 4♠ A♣ A♦ 10♥ 5♥ 3♦ 5♠
## Drawn: A♥ 3♠ 10♣ 8♦ 2♣ 2♠ J♥ 6♣ 6♠ 9♦
mydeck$reshuffle()
mydeck
## Not Drawn: 4♦ A♥ 8♣ 10♥ 8♥ 10♠ J♦ K♣ 5♥ J♠ 8♦ 7♠ 7♦ J♣ K♦ 9♦ Q♠ 8♠ 9♥ 9♠ K♥ 5♠ A♠ 3♠ 4♥ 5♦ A♣ 10♣ 6♠ K♠ 2♥ Q♦ A♦ 7♣ 2♦ 10♦ 3♦ 7♥ 2♣ Q♥ 3♥ 5♣ 2♠ 3♣ 6♦ J♥ 9♣ 4♠ 4♣ 6♣ 6♥ Q♣
## Drawn:
Beacuse S3 is not modify in place
4. Create an R6 class that allows you to get and set the current time zone. You can access the current time zone with Sys.timezone() and set it with Sys.setenv(TZ = "newtimezone"). When setting the time zone, make sure the new time zone is in the list provided by OlsonNames().
TZ <- R6Class("TZ", public=list(
tz=Sys.timezone(),
get=function() self$tz,
set=function(new_tz) {
stopifnot(new_tz %in% OlsonNames())
Sys.setenv(TZ = new_tz)
self$tz <- Sys.timezone()
}
))
tz <- TZ$new()
tz$get()
## [1] "US/Hawaii"
tz$set("US/Hawaii") # I wish
tz$get()
## [1] "US/Hawaii"
tz$set("Mars")
## Error in tz$set("Mars"): new_tz %in% OlsonNames() is not TRUE
tz$get()
## [1] "US/Hawaii"
5. Create an R6 class that manages the current working directory. It should have $get() and $set() methods.
WD <- R6Class("WD", public=list(
wd=getwd(),
get=function() self$wd,
set=function(new_wd) {
stopifnot(dir.exists(new_wd))
setwd(new_wd)
self$wd <- new_wd
}
))
wd <- WD$new()
wd$get()
## [1] "/Users/jmaloof/git/AdvancedR"
wd$set("../")
wd$get()
## [1] "../"
wd$set("Mars")
## Error in wd$set("Mars"): dir.exists(new_wd) is not TRUE
wd$get()
## [1] "../"
wd$set("/Users/jmaloof/git/AdvancedR")
wd$get()
## [1] "/Users/jmaloof/git/AdvancedR"
no modify in place?