Functional Programming
by Ayush Goyal
What?
It's a programming style/paradigm
f(x) = y
f(x)=y
Why?
Results in concise code, easier to reason, less prone to errors
Define What rather than How
Functions without side-effects
A huge focus on Immutability of values
When/Where?
Lets dive into some key concepts
First Class Functions
Functions are values
First Class Functions
#function passed around as value
def multiply_with_3(x):
return x * 3
mul3 = multiply_with_3
mul3(6)
=> 18
#function returning functions
def multiply_with_x(x):
def return_func(y):
return x*y
return return_func
multiply_with_3 = multiply_with_x(3)
Higher Order Function
Functions can be passed to other functions as they are values
Higher Order Function
#function with function as arguments
def multiply_with_3(x):
return x * 3
def add5_aftermodify(x,modfyingfunc):
return modifyingfunc(x) + 5
add5_aftermodify(5,multiply_with_3)
=> 23
Pure Functions
Pure functions are referentialy transparent and without side-effects
Pure Functions
#pure function
def identity(x):
return x
def square(x):
return x*x
#impure function
def print_square(x):
print x*x
import time
def bad_random(x):
return int(time.time() % (x - 1))
Anonymous functions
Create functions on the fly, without assigning names, useful for closures and Higher Order functions
Anonymous Functions
# anonymous function
lambda x: x + 3
# can be binded to value
z = lambda x: x + 3
def less_than_x(threshold):
return lambda x: x <= threshold
less_than_3 = less_than_x(3)
less_than_3(5)
=> False
less_than_x(3)(5)
=> False
Anonymous Functions
;; in clojure
;; function definition
(defn square[x] (* x x))
;; function bind to variable
(def square (fn [x] (* x x))
Awesome Example 1
\frac{df}{dx} = \frac{f(x+dx) - f(x)}{dx}
dxdf=dxf(x+dx)−f(x)
Awesome Example 1
from math import cos,pi
cube = lambda x: x*x*x
def derivative(func):
dx = 0.000000001
return lambda x: (func(x+dx) - func(x))/ dx
print derivative(cube)(2.0) #=> 12.0000009929
print derivative(cos)(pi/2) #=> -1.00000008274
deriavative_cube = derivative(cube)
Recursion
"In order to understand recursion, one must first understand recursion"
Recursion
;;define fibonnaci tree recursion
(define fib
(lambda (x)
(if (or (= 1 x) (= 0 x))
1
(+ (fib (- x 1)) (fib (- x 2))))))
;;define fibbonnaci iterative recursion
(define (fib-iter x y i n)
(if (= i n)
y
(fib-iter y (+ x y) (inc i) n))
(define (fib2 n) (fib-iter 0 1 0 n))
Recursion
;;define fibonnaci tree recursion
(define fib
(lambda (x)
(if (or (= 1 x) (= 0 x))
1
(+ (fib (- x 1)) (fib (- x 2))))))
;; tree recursion Fibonacci expansion
(fib 5)
(+ (fib 4) (fib 3))
(+ (+ (fib 3) (fib 2)) (+ (fib 2) (fib 1)) )
(+ (+ (+ (fib 2) (fib 1)) (+ (fib 0) (fib 1))) (+ (+ (fib 0) (fib 1)) 1))
(+ (+ (+ (+ (fib 0) (fib 1)) 1) (+ 1 1)) (+ (+ 1 1) 1))
(+ (+ (+ (+ 1 1) 1) (+ 1 1)) (+ (+ 1 1) 1))
;#=> 8
Recursion
;;define fibbonnaci iterative recursion
(define (fib-iter x y i n)
(if (= i n)
y
(fib-iter y (+ x y) (inc i) n))
(define (fib2 n) (fib-iter 0 1 0 n))
;; iterative recursion Fibonacci expansion
(fib2 5)
(fib-iter 0 1 0 5)
(fib-iter 1 1 1 5) ;; notice that at each step x(1st arg) is replaced with y(2nd arg)
(fib-iter 1 2 2 5) ;; and y is being replaced with sum of x and y
(fib-iter 2 3 3 5)
(fib-iter 3 5 4 5)
(fib-iter 5 8 5 5) ;; fib2 returns y(second arg) if i(third arg) and n(fourth arg) are equal
;#=> 8
Recursion
Tree Recursion flow
Let's discuss some general higher order functions before moving ahead
Map
# python
# imperative
array = []
for i in range(1,10):
array.push(x*x)
print array
#=> [1, 4, 9, 16, 25, 36, 49, 64, 81]
# functional
map(lambda x: x*x, range(1,10))
#=> [1, 4, 9, 16, 25, 36, 49, 64, 81]
# ruby
(0..3).map {|x| (Time.now - 86400 * x).strftime("%d-%m-%Y")}
# => ["07-03-2015", "06-03-2015", "05-03-2015", "04-03-2015"]
Reduce
# python
# 1 + 4 + 9 + 16 + 25 ......
# imperative
sum = 0
for i in range(1,10):
sum = sum + i*i
print sum
#=> 285
# functional
reduce(lambda x,y: x + y , map(lambda x: x*x, range(1,10)))
#=> 285
Filter
# python
# 4 + 16 + 36 + 64 ....
# imperative
sum = 0
for i in range(1,10):
if i % 2 == 0:
sum = sum + i*i
print sum
#=> 120
# functional
even = lambda x: (x % 2 == 0)
sum = lambda x,y: x + y
square = lambda x: x * x
reduce(sum,map(square,filter(even, range(1,10))))
#=> 120
Sorting
# python
array = ["a","quick","brown","fox"]
sorted(array)
# => ['a', 'brown', 'fox', 'quick']
sorted(array,key = lambda x: len(x))
# => ['a', 'fox', 'quick', 'brown']
import time
dates = ["29-04-2014", "28-04-2014", "21-04-2015"]
sorted(dates,key = lambda d: time.strptime(d,"%d-%m-%Y"))
# => ['28-04-2014', '29-04-2014', '21-04-2015']
Function Composition
You have kinda seen this one
#> cat apache.log | awk '{print $6}' | head -n100 | sort | uniq
Function Composition
# python
dec = lambda x: x - 1
inc = lambda x: x + 1
square = lambda x: x*x
def dec_double_inc(x):
dec(square(inc(x)))
dec_double_inc(4)
=> 10
Function Composition
But Lets take this one step ahead
Function Composition
# python
def compose(*functions):
return reduce(lambda f,g: lambda x: f(g(x)),functions)
dec = lambda x: x - 1
inc = lambda x: x + 1
square = lambda x: x*x
inc_square_dec = compose(dec,square,inc)
inc_square_dec(4)
=> 24
Function Composition
;; clojure
(def dec_square_inc (comp inc (fn [x] (* x x)) dec))
-- haskell
dec :: Num a => a -> a
dec a = a - 1
inc :: Numa a => a -> a
inc a = a + 1
square :: Numa a => a -> a
square a = a * a
dec_square_inc = (dec.square.inc)
Lazy Evaluation
Evaluate only when required
Lazy Evaluation
# generator expressions in python
def tokenize_file(path):
return (word for line in open(path) for word in line.split())
def count_words_in_file(path):
sum = lambda x,y: x + y
constant_one = lambda x: 1
return reduce(sum, map(constant_one,tokenize_file(path)))
# generator function
def drange(start, stop, step):
r = start
while r < stop:
yield r
r += step
Generator Expressions/Functions in python
Lazy Evaluation
(def fib (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
(take 10 fib) ;;(0 1 1 2 3 5 8 13 21 34)
(def fib2 (map first (iterate (fn [[a b]] (do (println "processing") [b (+ a b)])) [0 1])))
(take 2 fib2)
;processing
;(0 1)
(take 2 fib2)
;(0 1)
(take 10 fib2)
;processing
;processing
;processing
;processing
;processing
;processing
;processing
;processing
;(0 1 1 2 3 5 8 13 21 34)
(take 11 fib2)
;processing
;(0 1 1 2 3 5 8 13 21 34 55)
Lazy/Infinite Sequences in haskell/clojure
Awesome Example 2
\int_a^b f(x)\,dx = \sum_{i=0}^{n} \frac{b-a}{n} . f(x + i\frac{b-a}{n})
∫abf(x)dx=∑i=0nnb−a.f(x+inb−a)
Awesome Example 2
from math import cos,pi
cube = lambda x: x*x*x
def drange(start, stop, step):
r = start
while r < stop:
yield r
r += step
def integral(func):
step = lambda a,b: (b-a)/1000.0
sum = lambda x,y: x + y
prod = lambda dx: lambda x: func(x) * dx
return lambda a,b: reduce(sum ,map( prod(step(a,b)),
drange(a,b,step(a,b))))
print integral(cube)(0,3) #=> 20.20952025
print integral(cos)(0,pi/2) #=> 1.00078519255
Currying
mul :: (Num a)=> a -> a -> a
mul x y = x * y
mul_3 = mul 3
mul 5 6
--> 30
mul3 6
--> 18
:t mul
--> mul :: Num a => a -> a -> a
:t mul3
--> mul3 :: Num a => a -> a
-- Of,course you could've just done this
mul3 = (3 *)
Currying
from functools import partial
# emulating currying in python
def multiply(a,b):
return a*b
multiply_with_3 = partial(multiply,3)
multiply_with_3 = lambda x: multiply(3,x)
Most language don't support currying but there are alternatives
Questions
Resources
- Structure and Interpretation of Computer programs (SICP)
- The value of values and simplicity matters by Rich Hickey (doesn't require programming)
- Learn you a haskell for greater good
Intro to Functional Programming
By Ayush Goyal
Intro to Functional Programming
A brief slide to explain functional programming
- 1,754