Advanced R Exercise Solution (3)

Xinchen Pan · 2018/06/08

The exercise is getting harder.

OO field guide

Base types & S3

  1. Read the source code for t() and t.test() and confirm that t.test() is an S3 generic and not an S3 method. What happens if you create an object with class test and call t() 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.

  1. 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
  1. R has two classes for representing date time data, POSIXct and POSIXlt, which both inherit from POSIXt. 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
  1. 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"
  1. UseMethod() calls methods in a special way. Predict what the following code will return, then run it and read the help for UseMethod() 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.

  1. 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 of f and g?
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

  1. 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"
  1. 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.
  1. 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

  1. 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

  1. 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 classes extend() from envRefClass. 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"