Created
March 5, 2016 04:24
-
-
Save Gastove/c036a679cbde8872b62e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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