CMSC330

Higher Order Functions

Higher Order Functions

Higher Order Programming
Higher Order Functions
Common HOF

Higher Order Programming

Higher Order Programming

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}
          

Higher Order Functions

Lambdas

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)
          
Lambdas

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)
          
Lambdas

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

Lambdas

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 HOF

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

  • take in a function and a list
  • The list can be thought as the domain
  • the function is applied to each item in the domain
  • Returns new list of corresponding output
  • Basically maps domain to co-domain
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))
          
Map

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?