functions: an abstraction to encourage modular code:
Object-oriented programming is another kind of abstraction
Object-oriented programming is another kind of abstraction:
Classes: A template for an object (e.g. purdue
)
name
, puid
)Objects An instance of a class (e.g. varao
)
name = ‘vinayak’
)Methods Functions aware of properties of the object
isFaculty()
)1) Useful to group variables together
class
attribute setnew_purdue <- function(name, puid, employee ) {
obj <- list(name = name, puid = puid, employee = employee)
class(obj) <- 'purdue'; return(obj)
}
varao <- new_purdue('Vinayak', 1234, 'faculty' )
print(varao)
2) Tying methods to objects:
print
vs printMatrix
varao$employee ==’faculty’
vs isFaculty(varao)
)R has three OO systems:
We will concentrate on S3
Suppose varao
is an object of class purdue
Can write a function print.purdue()
and call when needed
Simpler/clearer to just use print()
Methods in classes
varao.print()
Generic functions
print(varao)
print.purdue()
but call print()
print
is a generic function that dispatches methodsIn most OOP languages, methods belong to objects
In R, methods belong to generic functions
UseMethod()
to call method based on object classmethods
gives you all methods associated with a generic
methods(print)
methods('[')
Can also give all methods associated with a class
methods(class= 'matrix')
ftype()
can tell generics from methods
library('pryr')
ftype(print)
ftype(print.data.frame)
Why do we need language support for OOP?
Can’t we just modify if conditions inside print ?
R’s OOP support allows
S3 can be viewed as a naming convention:
generic.class()
print.table
accessed via the generic print
print(varao)
will
print.purdue()
print.default()
print(varao)
print.purdue <- function(x) {
cat(x$name, ' (PuID:' , x$puid,') is ', x$employee,
' at Purdue\n' )
}
print(varao)
Inheritence
ab12 <- list(name = 'Alice' , puid = '12345' ,
employee = 'TA' , gpa = 3.8)
class(ab12) <- c('grad' , 'purdue')
print(ab12)
inherits(ab12, 'purdue' )
gpa.grad <- function(x) print(x$gpa)
#gpa(ab12) # We don ' t have a generic yet!
gpa <- function(x) UseMethod('gpa')
class(ab12)
gpa(ab12)
print(ab12)
Can also reuse methods using NextMethod()
print.grad <- function(x) {
NextMethod(print) # calls print.purdue
cat( ' \n Has GPA ' , x$gpa, '\n')
}
print(ab12)
print(varao)
We’ve seen how to write methods
To write a generic use UseMethod()
gpa <- function(x) UseMethod('gpa')
Essentially creates vector:
paste0(‘gpa.’,c(class(x), default)
Searches from left to right for function that exists
If it finds one, calls it, else returns error
Imagine a vector that you wanted to always view backwards
You want to hide from the user that it’s stored forwards
my_path <- c('right turn', 'cross street', 'climb stairs')
class(my_path) <- 'stack'
print(my_path)
print.stack <- function(x) print(rev(x))
print(my_path) # Are you surprised this works?
'[.stack' <- function(x,i) {
class(x) <- NULL # why do we need this?
x[length(x)+1-i]
}
# warning: this messes up your previous print function
my_path[3]
A powerful way to organize software
Allows you to build on existing software without changing it
Can avoid a bewildering set of new names for a generic task
S3 is a very informal system with no real checks
Can assign any class to any object
Can cause trouble if you’re not careful