Consider the following:
domain = [1,2,3,4,5]
codom1 = [2,3,4,5,6]
codom2 = [2,4,6,8,10]
codom3 = [False,True,False,True,False]
We need a function for all of this
We need a function for all of this
def add1(lst):
ret = []
for x in lst:
ret.append(x + 1)
return ret
def times2(lst):
ret = []
for x in lst:
ret.append(x*2)
return ret
def isEven(lst):
ret = []
for x in lst:
ret.append(x%2 == 0)
return ret
def add1(lst):
ret = []
for x in lst:
ret.append(x + 1)
return ret
def times2(lst):
ret = []
for x in lst:
ret.append(x*2)
return ret
def isEven(lst):
ret = []
for x in lst:
ret.append(x%2 == 0)
return ret
A lot of reused code
A lot of reused code
what is common?
def func(lst):
ret = []
for x in lst
ret.append(___)
return ret
The only thing that changes is the function
def func(lst,f):
ret = []
for x in lst
ret.append(f(x))
return ret
def func(lst,f):
ret = []
for x in lst
ret.append(f(x))
return ret
Can we send in a function as an argument?
def func(lst,f):
ret = []
for x in lst
ret.append(f(x))
return ret
Can we send in a function as an argument?
print(func(domain,add1))
Higher order programming: treating functions as data
Higher order programming: treating functions as data
def one():
return 1
def two():
return 2
print(one() + two())
one = 1
two = 2
print(one + two)
We bind values 1 and 2 to be used later
Higher order programming: treating functions as data
def foo(x):
return x + 1
def bar(x):
a = x + 1
return a * a
# how to do the following??
# foo = {get_input,input + 1}
# bar = {get_input,a = input+1,a*a}
def add1(x):
return x + 1
add1(3)
Lambda function: a function without a name
(lambda x: x + 1)
Consider the following
add1 = lambda x: x + 1
add1(3)
add1 = lambda x: x + 1
add1(3)
Syntax is different, semantics is the same
We can chain them
addNums = lambda x: (lambda y: x + y)
add2 = addNums(2)
add2(3)
#(lambda x,y: x + y)(2,3)
addNums = lambda x: (lambda y: x + y)
add2 = addNums(2)
add2(3)
#(lambda x,y: x + y)(2,3)
A function that returns another function
def app(f,x):
f(x)
app(lambda x: x + 1,3)
A function that takes in another function as an argument
addNums = lambda x: (lambda y: x + y)
add2 = addNums(2)
add2(3)
#(lambda x,y: x + y)(2,3)
def app(f,x):
f(x)
app(lambda x: x + 1,3)
Higher Order Function: functions that return a function or take in another function as an argument
Common and useful HOFs exist
We already saw one:
def apply_to_list(f,lst):
ret = []
for x in lst:
ret.append(f(x))
return ret
This is called map
def apply_to_list(f,lst):
ret = []
for x in lst:
ret.append(f(x))
return ret
This is called map
Basically maps domain to co-domain
map(f,lst)
(* map.py *)
add1 = lambda x: x + 1
x2 = lambda x: x + x
is_even = lambda x: x%2 == 0
lst = [1,2,3]
list(map(add1,lst))
list(map(x2,lst))
list(map(is_even,lst))
fs = [add1,x2,(lambda x: -x)]
list(map(lambda f: list(map(f,lst)),fs))
So common, python supports list comprehension
(* lstcom.py *)
lst = [1,2,3]
[x + 1 for x in lst]
[x * 2 for x in lst]
[x%2 ==0 for x in lst]
fs = [lambda x: x + 1, lambda x: x *2, (lambda x: -x)]
[[f(x) for x in lst] for f in fs]
Consider the following family of functions
(* ff.ml *)
def concat(lst):
ret = ""
for x in lst:
ret += x
return ret
def sum(lst):
ret = 0
for x in lst:
ret += x
return ret
def rev(lst):
ret = []
for x in lst:
ret.insert(0,x)
return ret
def filter(f,lst):
ret = []
for x in lst:
if f(x):
ret.append(x)
return ret
Aggregates a list to a single value
Has common code
Has common code
def common(lst,start,combine):
ret = start
for x in lst:
combine(ret,x)
return ret
This is called fold or reduce
This is called fold or reduce
def fold(lst,start,combine):
ret = start
for x in lst:
ret = combine(ret,x)
return ret
concat = lambda lst: fold(lst,"",lambda x,y: x+=y)
sum = lambda lst: fold(lst,0,lambda x,y: x+=y)
rev = lambda lst: fold(lst,[],lambda x,y: [y]+x)
filter lambda f,lst: fold(lst,[],lambda x,y: [y]+x if f(y) else x)
We can use map and fold together for cool stuff
Find the max of lists of lists?
Can we count 1s in a 2d matrix?
How could we rotate a matrix?