The exercise is getting harder.
OO field guide
Base types & S3
- Read the source code for
t()
andt.test()
and confirm thatt.test()
is an S3 generic and not an S3 method. What happens if you create an object with class test and callt()
with it?
t
## function (x)
## UseMethod("t")
## <bytecode: 0x0000000019ab3e08>
## <environment: namespace:base>
t.test
## function (x, ...)
## UseMethod("t.test")
## <bytecode: 0x00000000190b4970>
## <environment: namespace:stats>
ftype(t)
## [1] "s3" "generic"
ftype(t.test)
## [1] "s3" "generic"
Since object test does not have a method t()
associated with it. Calling t()
will call the default class.
- What classes have a method for the Math group generic in base R? Read the source code. How do the methods work?
methods(Math)
## [1] Math,nonStructure-method Math,structure-method
## [3] Math.data.frame Math.Date
## [5] Math.difftime Math.factor
## [7] Math.POSIXt
## see '?methods' for accessing help and source code
- R has two classes for representing date time data,
POSIXct
andPOSIXlt
, which both inherit fromPOSIXt
. Which generics have different behaviours for the two classes? Which generics share the same behaviour?
Basically the generics of those two inherited classes that exist in POSIXct
share the same behavior. The generics that only exist in one class has different behavior.
methods(class = "POSIXt")
## [1] - + all.equal as.character Axis
## [6] coerce cut diff hist initialize
## [11] is.numeric julian Math months Ops
## [16] pretty quantile quarters round seq
## [21] show slotsFromS3 str trunc weekdays
## see '?methods' for accessing help and source code
methods(class="POSIXct")
## [1] [ [[ [<- as.data.frame as.Date
## [6] as.list as.POSIXlt c coerce format
## [11] initialize length<- mean print rep
## [16] show slotsFromS3 split summary Summary
## [21] weighted.mean xtfrm
## see '?methods' for accessing help and source code
methods(class="POSIXlt")
## [1] [ [[ [<- anyNA as.data.frame
## [6] as.Date as.double as.list as.matrix as.POSIXct
## [11] c coerce duplicated format initialize
## [16] is.na length length<- mean names
## [21] names<- print rep show slotsFromS3
## [26] sort summary Summary unique weighted.mean
## [31] xtfrm
## see '?methods' for accessing help and source code
- Which base generic has the greatest number of defined methods?
library(pryr)
objs <- mget(ls("package:base"), inherits = TRUE)
funs <- Filter(is.function, objs)
generics <- funs[sapply(funs, function(x) ("generic" %in% ftype(x)))]
names(generics)[which.max(sapply(names(generics), function(x) length(methods(x))))]
## [1] "print"
UseMethod()
calls methods in a special way. Predict what the following code will return, then run it and read the help forUseMethod()
to figure out what’s going on. Write down the rules in the simplest form possible.
y <- 1
g <- function(x) {
y <- 2
UseMethod("g")
}
g.numeric <- function(x) y
g(10)
## [1] 2
h <- function(x) {
x <- 10
UseMethod("h")
}
h.character <- function(x) paste("char", x)
h.numeric <- function(x) paste("num", x)
h("a")
## [1] "char a"
g(10)
will return 2. Since y is not in the function, it will search in g
h(“a”)
will return “char a” because “a” is a character. h.numeric()
will not be executed or further searched.
- Internal generics don’t dispatch on the implicit class of base types. Carefully read
?“internal generic”
to determine why the length of f and g is different in the example below. What function helps distinguish between the behaviour off
andg
?
f <- function() 1
g <- function() 2
class(g) <- "function"
class(f)
## [1] "function"
class(g)
## [1] "function"
length.function <- function(x) "function"
length(f)
## [1] 1
length(g)
## [1] "function"
length(f)
will not search for length.function
thus “function” will not be returned unlike length(d)
. f
does not have a class associated with it so internal generics don’t dispatch on the implicit class of base types.
S4
- Which S4 generic has the most methods defined for it? Which S4 class has the most methods associated with it?
generics<- getGenerics(where = search())
generics_s4 <- generics@.Data[sapply(generics@.Data, function(x) isS4(get(x)))]
generics_s4[which.max(sapply(generics_s4, function(x) length(methods(x))))]
## [1] "coerce"
s4class <- getClasses(where = .GlobalEnv, inherits = TRUE)
Methods <- sapply(s4class, function(x) showMethods(classes = x, printTo = FALSE))
names(Methods)[which.max(lapply(Methods, function(x) length(grep("Function", x))))]
## [1] "ANY"
- What happens if you define a new S4 class that doesn’t “contain” an existing class? (Hint: read about virtual classes in
?setClass
.)
In the latter case, a virtual class may include slots to provide some common behavior without fully defining the object—see the class traceable for an example. Note that “VIRTUAL” does not carry over to subclasses; a class that contains a virtual class is not itself automatically virtual.
- What happens if you pass an S4 object to an S3 generic? What happens if you pass an S3 object to an S4 generic? (Hint: read
?setOldClass
for the second case.)
RC
- Use a field function to prevent the account balance from being directly manipulated. (Hint: create a “hidden” .balance field, and read the help for the fields argument in
setRefClass()
.)
Not sure
- I claimed that there aren’t any RC classes in base R, but that was a bit of a simplification. Use
getClasses()
and find which classesextend()
fromenvRefClass
. What are the classes used for? (Hint: recall how to look up the documentation for a class.)
classes <- getClasses(where = .GlobalEnv, inherits = TRUE)
classes[(sapply(classes, function(x) extends(x, "envRefClass")))]
## [1] "envRefClass" "refGeneratorSlot" "localRefClass"