Skip to content

Instantly share code, notes, and snippets.

@Gastove
Created March 5, 2016 04:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Gastove/c036a679cbde8872b62e to your computer and use it in GitHub Desktop.
Save Gastove/c036a679cbde8872b62e to your computer and use it in GitHub Desktop.
# Created 2016-03-04 Fri 20:20
#+TITLE: R
#+AUTHOR: Ross Donaldson
* Object Oriented, Functional, and Imperative Programming
Right OK: what are these three things? What do they do and how are they
different?
* Object-orientation
OOP is, as one might expect, oriented around /objects/. An "object" is a piece of
re-usable code -- something with functionality we might want to use over and
over without having to specify it each time. Typically, "object oriented
programming" pivots around /classes/ -- in some sense, the Nouns of
programming. An Object, or Class, can have /attributes/ (properties of a given
Noun, like the Name of a Person) and /methods/ (actions an object can undertake).
R actually has three different class systems; I find them to be slightly less
clear for demonstrating the notion of "methods on a class" -- and I don't
understand any of them particularly well :/ -- so here's a short example in
python:
#+BEGIN_SRC python
class Dog(object):
def __init__(self, breed, sound):
self.breed = breed
self.sound = sound
def bark(self):
print self.sound
def ima(self):
print "I'ma " + self.breed
#+END_SRC
Now, any time we want a ~Dog~, we can just make one or two:
#+BEGIN_SRC python
lab = Dog('labrador', 'woof')
spaniel = Dog('Prince Charles', 'As it were, woof')
#+END_SRC
And then we get a consistent interface, through methods:
#+BEGIN_SRC python
print lab.ima()
print lab.bark()
print spaniel.ima()
print spaniel.bark()
#+END_SRC
#+RESULTS:
: I'ma labrador
: woof
: I'ma Prince Charles
: As it were, woof
Or, through attributes (note that in python, the absence of ~()~ means we're
accessing an attribute, not calling a function):
#+BEGIN_SRC python
print lab.breed
#+END_SRC
#+RESULTS:
: labrador
In R, all kinds of things are objects too -- ~data.frames~, ~POSIXct~ date objects,
and on and on. And, while (to the best of my knowledge) R does support
~object.method()~ style calls, I've personally seen a lot more of this:
#+NAME: input
| breed | bark |
|----------------+------------------|
| labrador | woof |
| Prince Charles | As it were, woof |
#+BEGIN_SRC R
df <- data.frame(i)
write.csv(df)
#+END_SRC
That is, functions that take an instance of a class as an argument.
For our current discussion, the important part of Object Oriented Programming is
that it is /noun/ oriented -- what is the Thing we care about at each step.
* Imperative Programming
A natural next step, to me, from Object Oriented Programming is /imperative/
programming -- programming which describes a sequence of steps that
incrementally alter the state of a Noun.
#+BEGIN_SRC R
v <- c(1, 2, 3, 4, 5)
for (i in v) {
v[i] <- i + 5
}
v
#+END_SRC
#+RESULTS:
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
~for~ loops are about as imperative as you can get. One step; one change. In
python, you might see something like:
#+BEGIN_SRC python
class Person(object):
def __init__(self, starting_age):
self.age = starting_age
# Make a list of instances of Person
people = [Person(23), Person(30), Person(55)]
# Age everyone by 1 year
for person in people:
person.age = person.age + 1
# Collect a result
res = []
for person in people:
res.append(person.age)
return res
#+END_SRC
#+RESULTS:
| 24 | 31 | 56 |
A thing to notice: imperative programming frequently requires that data be
/mutable/. We change the age of a person by making that person one year older. In
our R example, we increment each integer in a vector by modifying each value in
that vector.
* Functional Programming
If OOP is noun-driven programming, Functional Programming is /verb/
driven. Generally, FP focuses on making small, purposeful functions, then
building them up in to larger functions (a technique called function
/composition/):
#+BEGIN_SRC R
double <- function(x){ return(x * 2) }
addFive <- function(x){ return(x + 5) }
enbiggen <- function(x){
plussed <- addFive(x)
doubled <- double(plussed)
return(doubled)
}
#+END_SRC
#+RESULTS:
In deeply functional languages like Clojure and Scala, composition is so
integral that there are functions just to compose other functions. We could do
that in R like so:
#+BEGIN_SRC R
compose <- function(funcs) {
funcIt <- function(x) {
accumulator <- x
for (func in funcs) {
accumulator <- func(accumulator)
}
return(accumulator)
}
return(funcIt)
}
inc <- function(x){ return(x + 1)}
double <- function(x){ return(x * 2)}
doubleInc <- compose(c(inc, double))
doubleInc(3)
#+END_SRC
#+RESULTS:
: 8
Working with tiny functions, we develop a set of virtues:
1. Referential Transparency: for a given input, a function can be removed and
replaced with that functions output and nothing should change about your program.
2. Immutability: To achieve #1, life is vastly easier if we /do not mutate/
data. A function returns a new value/data structure/whatever, instead of
modifying and old one.
In Functional Programming, we focus on Verbs -- so the inputs to our verbs
become Just Data. Data structures come in; new data structures go out. We do
vastly less defining of new types or classes, and vastly more "just make a
function that takes a data frame and gives back a slightly different data frame".
Also notice: in R, functions are "first class values", which means they can be
treated just like a string or an integer. A function can be returned from
another function, or stored in a variable, or passed as an argument to a
function -- something we do all the time with R's ~apply~ family of functions, or
with the assorted functions of ~plyr~ et al. Our Imperative ~for-loop~ that added
five to a vector of integers could also be:
#+BEGIN_SRC R
v <- c(1, 2, 3, 4, 5)
newV <- lapply(v, function(x){ return(x + 5)} )
newV
#+END_SRC
#+RESULTS:
| 6 | 7 | 8 | 9 | 10 |
Instead of step-by-step adding five, we just pass a five-adding function to a
function that does all the iteration for us.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment