Many ways to write a factorial function

Functional Programming: def factorialA(n):! fac = 1! FCO and HOF for i in range(n, 0, -1):! fac = fac * i! ! return fac!

def factorialB(n):! ! if n <= 0:! return 1! else:! def negatePredicate(p):! CS111 return n * factorialB(n - 1)! def negate(x):! This is new. We’ll return not p(x)! cover it today. return negate! Department of Computer Science from operator import mul! Wellesley College ! def factorialC(n):! ! return reduce(mul, range(1, n+1), 1)! 17-2

Imperative vs. Declarative Programming Everything in Python is an object

Every object in Python has a : In [1]: month = "April"! Imperative Declarative o Name In [2]: month! Expresses how computation is Expresses what the computation o Value Out[2]: 'April'! performed by explicit statements that accomplishes without showing how, o Type In [3]: type(month)! change the program’s state. leaving the details to the language. Out[3]: str! Programs use loops, conditionals, Programs are written by using certain Functions in Python are also objects,! they are called first-class building blocks that (mostly) don’t have ! variables and data structures (lists, dicts, objects (FCO). sets) that change during the program side-effects on data. When the building flow. blocks are functions, we get functional In [4]: len! programming. Out[4]: ! In [5]: type(len)! Python is a programming language that can do both these Out[5]: builtin_function_or_method! paradigms and mix them as needed. In [6]: len.__name__! Out[6]: ‘len’! 17-3 17-4 Functions as first-class objects [1] Functions as first-class objects [2]

In [1]: def add1(n):! o Functions can be passed as arguments just like integers, strings, or ...: return n+1! lists:! In [2]: add1! In [4]: words = [‘ab’, ‘abc’, ‘abcd’]! Out[2]: In [5]: map(len, words)! In [3]: type(add1)! Out[5]: [2, 3, 4]! Out[3]: function! ! ! We can store functions in data structures such o Functions can be returned as value by a function: In [4]: myList = [str, len, add1]! as lists. def linear(a, b):! In [5]: myList[1]! def result(x):! Out[5]: ! We can assign a return a*x + b! In [6]: myFunc = myList[2]! function to a variable. return result! In [7]: myFunc(3)! During invocation, name myFunc refers taxes = linear(0.3, 2)! Out[7]: 4! to function assigned to variable, add1! taxes(10000)! ! 17-5 17-6

Higher Order Functions Sorting tuples

o Functions that take at least some arguments that are themselves functions o How did we sort this list of (name, age) tuples by the 1st and/or items (names)? o Functions that return functions tupList = [('Clinton', 68), ('Sanders', 74),! ('Trump', 69), ('Cruz', 45)]

o In the following slides, let’s see examples of both. ! o How did we sort it by the 2nd items in the tuples (ages)?

17-7 17-8 Sorting tuples Sorting with "key" argument

st o By default, sorted() looks at the 1 items o sorted() can take an argument that specifies what to o Old solution ("DSU: decorate, sort, undecorate"): compare o Create new list of tuples with age as 1st item o "key" named argument is a function that gives the relevant [(68, ('Clinton', 68)), item of a tuple (74, ('Sanders', 74)), (69, ('Trump', 69)), (45, ('Cruz', 45))]! In [1]: def getItem2(tup): o Sort this list return tup[1] [(45, ('Cruz', 45)), ! (68, ('Clinton', 68)), In [2]: sorted(tupList, key=getItem2)! (69, ('Trump', 69)) Out[2]: [('Cruz', 45), ('Clinton', 68), (74, ('Sanders', 74))] ('Trump', 69), ('Sanders', 74)] o Map the list back to (name, age) tuples ! [('Cruz', 45), ('Clinton', 68), ('Trump', 69) ('Sanders', 74)] 17-9 17-10

How would you draw these pictures? Plotting a function

def plotFunc(func, xLo, xHi, step):! """plots values of func from xLo to xHi ! at intervals of step"""! inputs = np.arange(xLo, xHi, step)! outputs = [func(input) for input in inputs]! plt.plot(inputs, outputs, 'r-')! plt.show()!

o No way to define this without taking a function as an argument

17-11 17-12 How would you draw these pictures? Higher order list operations: map, filter!

o Higher order functions are particularly useful for operating over lists o Python has some built-in higher order functions

Will$do$these$in$lab!$ o Map paradigm: o List comprehension: [str(x) for x in numList]! o map function: map(str, numList) ! o Filter paradigm:

o Parameterize over trunk shape def isEven(x):! o Shapes are drawn by functions return x%2==0! o Simple shrub's trunk shape: o List comprehension: [x for x in numList if isEven(x)]! o Filter function: filter(isEven, numList)! fd(trunkLength)! ! 17-13 17-14

Higher order list operations: map, filter! Higher order list operations: reduce!

o New paradigm. No list comprehension equivalent! o Why use map and filter instead of list comprehensions?

o Example: product of numbers in a list mul(a, b) gives a * b o Although map and filter can always be expressed with list add(a, b) gives a + b!

comprehensions, most other higher-order functions cannot In [7]: from operator import mul, add

o Good practice with higher order functions In [8]: reduce(mul, [1, 2, 3, 4, 5]) ! Out[8]: 120 ! o Example: sum of numbers in a list

In [10]: reduce(add, [1, 2, 3, 4, 5]) Out[10]: 15 ! Equivalent to built-in sum()!

17-15 17-16 Higher order list operations: reduce Higher order list operations: reduce o How does reduce work? o How does reduce work?

reduce(mul, [1, 2, 3, 4, 5]) reduce(mul, [1, 2, 3, 4, 5]) ! !

1! 2! 3! 4! 5! 1! 2! 3! 4! 5!

mul! mul!

2! 2! 6! 17-17 17-18

Higher order list operations: reduce Higher order list operations: reduce o How does reduce work? o How does reduce work?

reduce(mul, [1, 2, 3, 4, 5]) reduce(mul, [1, 2, 3, 4, 5]) ! !

1! 2! 3! 4! 5! 1! 2! 3! 4! 5!

mul! mul!

2! 6! 24! 2! 6! 24! 120! 17-19 17-20 Nested Functions: motivation Nested Functions: motivation

o Write a function that takes a list of words and a character c, o Functions defined within functions have access to all local and returns the list of all words that begin with c. variables of the parent function o List comprehension:

def filterStartsWith1(c, listOfWords): o Solution for filter problem in previous slide: return [word for word in listOfWords if word[0]==c] def filterStartsWith2(c, listOfWords): c is in this function's ! def startsWith(word): o filter: return word[0] == c return filter(startsWith, listOfWords)! def filterStartsWith2(c, listOfWords): return filter(??, listOfWords) ! o Predicate passed as argument to filter should take a single word as the argument

17-21 17-22

Note: lambda to avoid nested functions Modeling Nested Functions: scale def testNested(factor, arg1, arg2): o startsWith is used only once def scale(num): How to explain the evaluation of return factor*num! testNested(10, 2, 3)? o We can define "anonymous" functions for one-time use with return scale(arg1) + scale(arg2)!

lambda (more on them next lecture) A nested function value is a so-called closure that has two parts:

(2) An environment = a dictionary of local o No need for nested functions variables other than the parameters used in the (1) The function definition body of the function definition def scale(num): factor' 10 def filterStartsWith3(c, listOfWords): return factor*num! return filter(lambda word: word[0]==c, listOfWords)! ! scale(3) When the nested function is called, all the key/ value pairs in the environment are added to num' 3 factor' 10 the variable section of the function frame, along with the parameters return factor*num

17-23 17-24 Modeling Nested Functions: startsWith Functions that return functions def filterStartsWith2(c, listOfWords): def startsWith(word): return word[0] == c o Python also lets you define functions that return functions. return filter(startsWith, listOfWords)!

In filterStartsWith2('b', ['ant', 'bat', 'cat', 'bunny', 'dog']), the o In the notebook, define a function called closure for startsWith has these two parts: randomFunction that takes a list of functions and returns a random one (1) The function definition (2) The environment

def startsWith(word): c' ‘b’ return word[0] == c

! startsWith(‘ant’) What happens when startsWith is called on word' ‘ant’ c' ‘b’ ‘ant’? return word[0] == c

! 17-25 17-26

Functions that return functions: Functions that return functions: negatePredicate! negatePredicate!

o A predicate is any function that returns a boolean (True o Let's define a function, negatePredicate, that takes a or False) predicate and returns the inverse of that predicate

o Let's define a function, negatePredicate, that takes a o Whenever the predicate returns True, the inverse of the predicate predicate and returns the inverse of that predicate returns False, and vice versa

o Whenever the predicate returns True, the inverse of the predicate returns False, and vice versa def negatePredicate(pred): def negatePredicateApply(x): return not pred(x) return negatePredicateApply!

17-27 17-28 negatePredicate on isEven! Converting between functions objects and def isEven(n)! function names as strings return n%2 == 0 ! def negatePredicate(pred): o For function name ! , use eval def negatePredicateApply(x): return not pred(x) o In [2]: func = eval('len')! return negatePredicateApply! o In [3]: type(func)! ! isOdd = negatePredicate(isEven)! o Out[3]: builtin_function_or_method! o In [4]: func('test')! (1) The function definition (2) The environment o Out[4]: 4! isOdd' def negatePredicateApply(x): o In [5]: eval('hdwgdjw')! pred' return not pred(x) ------!

! NameError (1) The function definition (2) The environment (empty) Traceback (most recent call last)! def isEven(n)! in ()! ----> 1 eval('hdwgdjw')! return n%2 == 0 17-29 17-30 ! ! o in ()!

o NameError: name 'hdwgdjw' is not defined!

Program with menu of options Program with menu of options o calculator.py displays Enter a number: 5! o Could we do this without all the if-statements? a menu of available Enter another number: 2! o What if operate did not know the possible values of arithmetic functions, takes What would you like to do funcname? user input on which function with these numbers?!

to use, and prints the result. # add! # subtract! o Here's a solution (in calculatorHigher.py)

# multiply! o Flesh out the operate # divide! def operate(a, b, func): function. Enter your selection: return func(a, b)! subtract! The answer is 3.0!

17-31 17-32