[MUSIC] [BLANK_AUDIO] In this video, I'm going to talk about higher-order functions. So what is a higher-order function? Well, it's a function that can take other functions as arguments or can even return functions. Huh. Right, that's interesting, okay? What does that mean exactly? Well, the higher-order function, now, it gets a function as an argument and then it can call that function. This allows it to implement much more sophisticated behaviors. So this is a very powerful concept that allows me to write much more interesting programs. Okay, and instead of repeating a bunch of similar behaviors where you do slightly different things, instead you can encapsulate this into one higher-order function, and then modify its behavior by passing it in different functions. Think about that for a minute, all right? This is definitely a powerful concept, so let's take a look further. We know how to build functions, so let's build some simple functions. I'm going to build a function double. Okay, and that returns double of its input. And, I want to return, I want to write a function called square, that squares its input, okay? And we know how these work, so let's say print double. I gotta type print correctly. [LAUGH] Double three, print square three. Alright, let's run this. Nothing surprising here. Okay, I doubled three. I squared three. Woo hoo. Great, I know how to write a function. Now, let's write a higher order function. Twice, func, val, and what I want this thing to do is to return func of func of val. All right. Why is this a higher-order function? Well, a higher-order function either takes a function as an argument, or returns a function. This function here is taking a function as an argument. That first argument, func, I am expecting you to pass me a function. Alright? And you can see what's going to happen. If I pass you a function, I'm actually going to call it twice. I'm going to call func on val. I'm going to take the result, and I'm going to call func on the result. So let's actually try this. And let's see what happens. Print twice. And what do I want to do twice? I want to double something twice. And lets double the number three twice, okay? Think about what you, what you expect to happen here. I'm going to call the function double on three and then I'm going to call the function double on its result. Alright, okay, I get 12. It doubled it, I got six. Doubled it again, I get 12. Alright. Okay. Do not just watch by and say, oh yeah, that makes sense. No problem. No, no, no. Okay? I passed a function, double, as an argument, okay? It now has a new name. Its now called func, not double and I actually called it twice inside of this function. So why did I define twice? That's kind of stupid, I could have just, you know, called double twice instead of doing this. But look what I can do now. Let's square three. I now have the ability where I can call any function twice, right? So did that work? Square three is nine, square of that is 81. Yes it does. So I now have a new function twice, that takes any function you want and calls it, and then calls it again on the result of the first call, okay? Of course, this function has to take a single argument, but other than that, you know, we've got an interesting situation here. Now. Right, now that we understand that we can use functions as arguments, let's think of, look at some other things that we can do. Let's imagine I have a list of data, 1, 3, 6, 9, 18. Okay, and let's imagine that I want to double everything in there. Well, I know one way of doing that. I can say newdata equals, let's use a list comprehension, double item for item in data. Print newdata. Let's see what happens there. Okay, when I run that, all right, I get a list with 2, 6, 12, 18, 36. No surprise. Okay, I know how to do that. And now I have a list with everything doubled in it. Well, this is such a common idiom, there's actually a function in Python and many other languages, too, that allows me to do this. I'll say newdata2 equals map, alright? That's the name of the function, and it takes a function as an argument and a list. Alright, so what's going to happen is map takes this, this first argument, this function, and it applies it to every element in the list and creates a new list with all the results, right? Just like that list comprehension did, and let's print newdata2 and let's look at it. Alright. I got 2, 6, 12, 18, 36, as I would expect. I can pass different functions in here to map. Let's pass square, and see what happens. Alright? Now I have 1, 9, 36, 81, 324. Alright, that looks like it worked. Okay, so map is a powerful function that allows you to manipulate lists, right? By applying a particular function to every element to the list to create a new list, right? This is very similar to what I could do with a list comprehension, alright? Except that I need to use a function, whereas in list comprehension I could just use an expression. Alright, there's another function like this called filter. Alright, let's define a new function, even, if val percent two, then return false because it's odd, else return true. Now I have a function that returns true if something's even, false otherwise. Let's look at newdata3 equals filter, even, data, and see what happens. Print newdata3. Okay, I do that. I'm going to run it. I want you to think about what you think might happen. I'm taking a function in a list. Map maps a function onto every element of the list. Filter is going to filter somehow. Alright, what does that mean? Alright, it only keeps the things that were true. Right? So newdata3 is all of the elements of data that, for which the function even returned true. Okay, so between map and filter, I can filter a list by only getting things that satisfy some predicate. And by map, I can, you know, manipulate every element of the list. Okay, so you've seen how map and filter are actually built-in functions in Python that are higher-order functions. They allow you to do some interesting things to lists. But that's not all I can do with higher-order functions. I can pretty much do anything I want. So let's look at this function called area. I want to write a function that computes the area under a curve between two boundaries, a low and a high boundary. Alright, so I wrote a function called area. It takes four arguments. The first is the function that I'm trying to compute the area of, then a lower bound and upper bounds, low and high, and a step size because I'm going to actually approximate this. Okay, what this function does is it starts at the lower bound and it increments the current location by the step size again and again until you hit the upper bound, or high. Alright, and what I do is I evaluate the function, and I approximate the area under that curve as a rectangle of width step size. Okay, so I evaluate the function, multiply it by step size, take that rectangular area, I add it in to the total. I keep doing this, and when I've hit the upper bound, or high, I am done, and I return the total. All right? Let's try this out. Okay, let's define some functions. Let's define f of x, that just returns x. So that's basically f of x equals x, so my g of x is return x squared. So that's g of x equals x squared. And let's, let's see if we can print out the area. The area of f from zero to ten, with a step size of 0.01. Let's run that. Returns 50. All right. For those of you who know, who know calculus, you should be able to tell me immediately what the area under that curve is. Even if you don't know calculus, you should still be able to tell me immediately what the area under that curve is. Right? It's a triangle. f of x equals x from zero to ten, all right, is a triangle with side ten. You know? The base length is ten. The height is ten. And so it's one-half times base times height, 50. Alright, well, I got a reasonable approximation there. It's a little bit off, because, well, I'm approximating. Right? Let's print the area of g from zero to ten. Same step size. Okay. And 333. Again, those of you who know calculus, you should be able to calculate that out fairly easily, and figure out that 333 and a third is pretty much the answer here. And again, there's some error, because this is an approximate way. And I can reduce the error by changing the step size. Okay. And I get closer to 33, 333 and a third by doing that. Now, I don't have to have nice functions here. Okay, let's define h of x. All right, this function, if x is less than 3, let's return x, elif x is less than seven, let's return x squared, else, let's return seven times x minus four. Okay? And let's print the area. This function, from zero to ten, with, hm, let's use that step size. Okay, and oops. I did not type this right. The area from zero to ten with that step size, lets run. And I get 276.3. Is that correct? I have no idea. I leave it to you to calculate it and see if it actually did the right thing. Okay? So I can create functions like this that take other functions and manipulate them or evaluate them at certain locations to do interesting things like compute the area under that function. Yeah, I can use this for a variety of different conceptual ideas, not just doing something like this. Hopefully after watching this video, you have an appreciation of higher ardor, order functions. We can write functions that use other functions, right? This is how I can write that area function where I can take the area under an arbitrary curve, right? All I have to do is pass it a function. This is such a powerful concept. There are many built-in functions in Python that do this, right, like map and filter. They use these functions in a very well-defined way to accomplish specific behaviors, right? But now you also know how you can write your own higher-order functions. So, this is a new tool in your toolbox that will allow you to write much more sophisticated programs.