 In this video, we are going to look into further examples of how to use recursion and looping strategies to implement iteration. That is, executing code repeatedly. And we are going to use an example that you all know from your high school math, which is called the factorial. So let's start a new file and rename it into simply factorial. And so now, before we go into the coding part, I would say, let's go ahead and look at a couple of examples to review your math knowledge. And now in a markdown cell, I'm going to use a couple of times in this video and also in the next couple of videos, the dollar symbols. And basically what I use them for is that the formulas that are included are formatted in a nice way, just as you know it from academic journals and so on. It's a language called laytag, but you don't need to know this language for this course. So let's go ahead and look at an example. Let's look at the example of three factorial written like this. So what is three factorial? Well, it is the product of the numbers from one through three. So some of you may learn this as follows. Some of you learned it as one times two times three, which of course is six. Okay, that is three factorial. Some other people watching this video may have learned it the other way around. So three times two times one, which is of course the same as one times two times three. It's perfectly symmetrical. So it really depends probably on your high school teacher on which way you prefer. But we are going to see that these are two different ways of viewing the same problem. And depending on how we view the problem, a different strategy when coding may be more intuitive than the other. Okay, but now let's go further into the mathematical discussion, so to say. So let's also go ahead and now try to generalize this expression. So one thing that I always was confused with in high school was, what is the factorial of zero? Well, your high school teacher will have told you probably that it is defined to be one. And note how I use the colon equals and not simply the equals, because I'm simply saying zero factorial is defined to be one. So it is not like there is no calculation for this to derive that. It is really a definition by mathematicians and it is a very smart definition, of course. And so just like mathematicians define zero factorial to be one, what they also do is they define factorial in a general way like this. So they say n factorial and n factorial is defined usually as n times, and then in parentheses you write n minus one, and then after the parentheses you have the factorial symbol again. And this is usually what you see in a good math course. So I know that sometimes, especially in high school when the teacher does not want to get too theoretical, what they sometimes do is they go ahead and write it like this. They write n times n minus one times dot dot dot times one and that's it. So sometimes math teachers also write it like this. This is probably more familiar, but note how the part up here, the first part, so the n times n minus one factorial part, this is really the formula that is important. And technically speaking, this is also just a definition. So in other words, this is what a mathematician would define. And now let me also remove the second half. I only included this to show you a more familiar picture so that you have an easier time understanding what this formula means, but this is really the generic definition of factorial. So maybe let's go ahead and say in general. So that is a concrete example and that is the generic rules. So now why is it that important to derive those generic rules? Well, what we can see here is basically the pattern of a recursion in the mathematical definition. So how do we see that? Well, if we look at the second line where I say n factorial is defined to be n times n minus one factorial, what am I really doing here? What are mathematicians doing here? Well, they're basically defining the factorial of a number, of a number n, in terms of the factorial itself. So see how the exclamation mark, the factorial operator, so to say, is on both sides of the definition on the left-hand side and also on the right-hand side. So I'm defining an operation in terms of itself. And that is just the definition of a recursion because in an earlier video we saw that a recursion is just a function that calls itself and has a way out. So where is the way out? Well, the way out is, of course, the first case here. Zero factorial is defined to be one. In this case, so when I'm calculating the factorial of the number zero, I just know it's one by definition. I don't have to do any calculations. That is usually what we refer to in a recursion as the base case. It is the way out. So let's see how we can take our knowledge from a recursion from the previous video when we talked about a simple countdown and how can we apply the recursion strategy to model a factorial. So now let's go ahead and now let's write a function. So let's call the function simply factorial. Of course, the function takes one parameter. Let's call it n also because it's a natural number. So it makes sense to call it n. And let's also write a nice doc string. So let's say calculate the factorial of a number. And now let's go ahead and also specify the arguments that the function takes. It's going to be an n. It's going to be an integer. And it's simply the number to calculate the factorial 4. And now that is a big difference to the recursions we saw in the previous video. Now, of course, we are going to have a return value. And the return value is also going to be an integer. And it's simply the factorial. So oftentimes I have a hard time to find a nice name for what I'm going to write here in the returns part. Well, often what you do is simply copy paste whatever the name of the function is. So the function takes an n. It gives us back the factorial of n. Both the input and the output are an integer. That's important. The factorial function would also work in some circumstances for floating point numbers. But this is mathematically a bit more involved. So we're not going to do that in this video. So let's now go ahead and simply translate the mathematical definition of a factorial in Python code as a recursion. So in other words, whenever we want to calculate the factorial of 0, we know it is 1 by definition. So let's write that. So let's simply go ahead and write if n double equals 0. So if the n that is passed in is 0, then we simply return 1. And that is now the big difference to the previous example where we modeled a countdown. Now we have a return value. So this function now has actually output. The previous function did not have output. But they were not yet done. So let's go ahead and also model the else clause. And now we are going ahead. And we have to break down this part here, the right-hand side of the formal definition here. And let's do the right-hand side first. So what I mean is the right-hand operator of the multiplication sign. So let's say the factorial function calls itself with n minus 1 as the argument. And whatever we get back from this function, we store in a variable. Let's call it simply recurs. I just give it a name. I made that up. So the function in the else clause first goes ahead and calls itself with a decremented argument, decremented by 1. It stores the result. And then we have to still calculate the overall result of the recursion. And let's do that like this. Let's introduce a variable called result. And the result is simply n times whatever recurses. Okay? Whatever we get back from the factorial function. So n times recurs, this is like n times the recursive side. The recursive step. And then, of course, we have to return something. So we are going ahead and we are going to return the result variable. So let's see first if this function works. So in order to check a function, it makes sense to think of edge cases. So two edge cases, we have to model it, of course, the factorial of 0. And this should be 1. So let's check. And indeed, the function gives us back 1. Okay? So also let's check what is the factorial of 1. 1 is kind of an edge case, right? Kind of, not really, but kind of, it also gives us back 1. And let's also go ahead and see what is the factorial of 3. So a number that is strictly greater than 1. So in other words, now the recursive step will have to be executed several times. And this basically gives me back 6. Which is, of course, also the correct result regarding the example of above here. So now let's try to further understand what the recursion does here. And to do so, let's copy paste the code over to Python Tutor. And this will be very helpful. So let's say result will be set to factorial. And let's also calculate the factorial of 3 here. And let's also remove doc string so that this becomes a little bit easier to read. Okay? Click on Visualize Execution. Now, we don't have this white area up here where it says print output because we're not using the print function. And usually, just a remark here, you never want a function to print something out. Very rarely you want that. So usually, a function takes input and it returns some output. That we then go on and do some further processing with. So let's see how the recursion works here. First step, the factorial function is defined. Okay? So the entire depth statement is run as a whole. This creates a new name in the global scope called factorial. It references an object. And the object here contains the entire code that is in the code block here. Let's call the function. This creates a new function scope where n is set to 3. And now the function begins to run from top to bottom. And because n is 3, the if condition is of course false and the function will go right into the else clause. And now comes something interesting. The first line in the else clause is recurs is set to the return value of factorial of n minus 1. So we know that the assignment statement is basically evaluated such that the right-hand side is run first. So the name recurs is not even read, so to say. So the right-hand side is run first. What this is is another function called to the same function. So this will create another scope. Okay? Where n is now 2. Okay, so far we haven't even multiplied. Okay? So next, what's going to happen is the next function called, so the first function called is now waiting on the second function called to return, some result. Now the second function called is going to start from top again. So the first function called is pausing at this first line in the else clause. The second function called starts to run from top to bottom again. So let's do that. And of course the if condition is still false because n is greater than 0. So what we do is the recursive step is done a second time. So let's go ahead. What this will do is it will simply create another function scope where n is now set to 1. And now the second function called is waiting for the third function called n is 1. That means the first condition is still not true. So we go into the else clause one more time. And now we will also execute the right hand side of the assignment statement one more time. This will create another function scope where n is set to 0. And now something interesting happens. So the first time something interesting happens. So now the if condition is true. And that means this function called down here where n is set to 0. This will now return 1. Okay? And that means the return statement is executed. And this will now as we see here create the return value of this function scope and the set to 1. And the return value is the only thing the only object that is going to survive after this function scope is basically removed. Okay? So Python automatically removes all the function scope the functions that are done. And everything inside the function scope all the names, all the objects that are referenced by variables inside are going to be removed. Going to be garbage collected. The only thing that survives is the return value. And that is different to the previous video where we modeled the countdown function. And in the countdown case we did not have a return statement. And therefore we automatically had a return value of none. The special object none. But here we have a return value. In this case 1. So what happens with this 1? The 1 is going to be returned to the previous function call the previous function call is still waiting at this line here. The line recurs is going to be set to the return value of factorial of n minus 1. And this is exactly what's going to happen. The one that is returned from the lower most function scope is now going to be set to the recurs variable. And now this function call the second to last one so to say is now going ahead with its calculations. So in the next line n times recurs is calculated and n times recurs in this case would be 1 times 1. And 1 times 1 is simply 1 again. So let's go one step further. We get a new variable called result which is now set to 1. Next line is going to be executed return result and return result will simply return this one. That means this function scope also has a return value and now in the next step this one here which is the same as the one of the result here this one is going to be returned to the function scope before that is still waiting and the other object as I said before will totally be gone this will be garbage collected. So this one the return value coming from the result variable will simply go one step up and become there of course the recurs variable because the second function call where n was set to 2 is still at this line here. So it was still waiting all the time. There were two further function calls but in all the time it was waiting at this line here and now it's going to continue it's also going to calculate n times recurs in this time it is 2 times 1 and 2 times 1 gives me 2 of course and this result is now going to be returned as the return value. So everything is now going to be lost except for the return value the 2 is going to move upwards and it becomes the recurs variable so now we have recurs as 2 this function scope the very first one that we started now the result variable is going to be calculated n times recurs this is going to be 6 of course and the 6 is going to be returned and it's going to be stored into a global variable called result that's what we do here and all the function scopes go away. So let's briefly recap when we start from the beginning we first create the function and then we make one function scope for the initial function call what's going to happen is we get further function scopes that is the recursion and all of them now have a return value and the previous function call will always wait until the return value comes back and then it will use it to calculate its result, its return value and then its return value is going to be returned to the previous function call and this is again how recursion works but now the big difference again being now we have return values okay so let's go back to JupyterLab and simplify this function a little bit so one thing that we can simplify is the following here we are going ahead and we are calculating something using the recursive step the recursive function call and the result of that is stored into a variable recurs and the variable recursive is only used in the next line for multiplication so one way to simplify this is as follows we could simply go ahead and copy or basically cut the function call and simply override the recursive variable and this makes the recursive variable basically redundant, we don't need it we can simply remove it similarly speaking now we set the result of this expression of n times factorial n minus 1 we set that to the result variable in the local scope we don't need this, we could actually simply go ahead and cut this out put this after the return line and simply get rid of the result variable let's see if it still everything works and we get the same results so let's quickly see what is the big difference in Python tutor here so this here would be n times and let's copy paste down the factorial here and let's get rid of the two variables here so this is what it looks like if we execute this version now the only difference is the initial function call where n is set to 3 this will jump into the else clause and the else clause executes the return n times factorial of n minus 1 so the expression on the right hand side of the return has to be evaluated first and n times can only be executed if we know what the return value of this is so in other words the right hand side of the multiplication is going to be executed first which creates another function scope okay so now the same happens as before same couple of function scopes as we did previously and now when we call factorial with n set to 0 what this does is it will simply return 1 right away okay and what happens to the 1 well the 1 is in the previous function call it simply used as the return value basically of factorial of n minus 1 and then basically the next the previous function call simply goes ahead and returns the result of n times whatever we got back so in other words we don't have any temporary variables here anymore but simply we jump right from return value to return value so the functions return a bit quicker we don't have temporary variables other than that nothing has changed and now let's make one more simplification that is maybe not so easy to understand at first but then it's again rather easy so in chapter 2 when we introduced the concept of functions all the functions we saw there only had one return statement that is different in this function call or in this function definition here we have two return statements so in chapter 2 the return statement was always in the last line of the function okay we calculated something and then the function returned the result here we have two return statements because we have two cases we have the base case and we have the recursive case so now what you need to know is whenever a function call is the return statement this particular function call is over right away the thing that is after the return statement is going to be returned but the function is done right away in other words whenever the if condition is true and we go into the line where it says return 1 anything that comes after it let's say for example I make 3. here any code that comes after it will not be executed anymore the function is done after it hits the return statement therefore anything also that would be after the if statement down here would also not be executed if the condition is true that's the reason here okay in other words what we could do is we could do the following I could simply go ahead and remove the else clause and simply unindent the return n times n times factorial n minus 1 and write it like this for now I simply keep an empty line to make it a bit easier to read so in other words this lower return statement is now only executed if this condition here the n equals zero condition is false because if it is true then we hit the return statement and as we just learned anything after a return statement is not executed right once we hit the return statement the function is done so in other words we could actually also take out this empty line and use the function just like this and this function does exactly the same as before and now something remarkably is to see to be seen so here we have the mathematical definition of a recursion the base case and the recursive case and down here I have the implementation Python which is basically the closest you can get with code to math world so here we have the base case we have to check for the condition is n zero if so the result is one otherwise the result of n factorial is whatever n times n minus 1 factorial is and this basically the second line n times n minus 1 factorial is basically the same as this n times factorial of n minus 1 so this is as close as we can get with code to a mathematical definition so that is solving the factorial function the factorial problem using a recursive approach and now let's also in this video talk about the alternative way because when I started with chapter 4 I said iterations of the idea of executing code repeatedly can be achieved using two different strategies one of them is recursion the other one is of course looping so let's go ahead and say alternative with looping so now the question is what can we do or how can we model the same problem here using a loop well this is rather easy to do so one thing you see is when I when we look at the example of 3 factorial we see that the answer to 3 factorial is either 3 times 2 times 1 or it is 1 times 2 times 3 no matter which way we view it it basically we can view it either way and both are correct so in other words if I see the sequence 1 2 3 well this is basically a range object here so let's say let's maybe go down here if I write range from 1 to 4 and let's say maybe I go ahead and put the list constructor around it this gives me the numbers 1 2 3 and all I want to do is I want to multiply them together so let's go ahead and instead of using the list constructor we loop over that for I in range 1 through 4 for now let's say print I this gives me the numbers on a line on their own but now I want to basically multiply them together so what can we do with it what can we do to achieve that so in our very first Python example in this course in chapter 1 we basically use the for loop to sum up all the even numbers if you remember we initialize the variable called total to 0 and then in the for loop we simply add up all the even numbers so now let's do something very similar so let's go ahead and introduce a variable let's call it product and let's initialize it with not to 0 but 1 and the reason why is because 1 is the neutral element of multiplication so whatever we multiply with 1 doesn't change and now let's go ahead and inside the for loop in this range we go ahead and we say the new product the updated product is the old product times I and then at the end let's simply look at product what it is and indeed it is also 6 so this approach is now the looping approach with which we solve the same problem so let's go ahead and make this a function so what I'm going to do is I'm going to copy paste the header from the function above let's go ahead put it right here let's indent everything into the function body let's rename the function into factorial loop so that we have a different name for it and now we have to parametrize this here so what we have to do is we have to replace the for with something with an n here because we have to not loop until a 4 or 3 in this case but we have to loop until n however we want to include the n okay so let's say I leave it for now with just n here in the range building and let's say below the for loop I'm simply going to return the product as the result if I now go ahead and call the factorial loop function with 3 I only get 2 not 6 and the reason why is because I'm only looping over the numbers 1 and 2 here okay I'm not looping over 3 if I pass in 3 for n then as we learned the range built in only loops from the left side including but the right hand side the second argument is excluded so in other words what we have to do is we simply have to add 1 to that okay if we add 1 to n in here then the factorial function in the looping version also works correctly and now maybe you are a bit confused of what are we really doing here but what we can always do is we can always use the print function inside a function and output or print out some intermediate steps so to say so what I'm going to do is I'm simply going to print out the product and we do that on one line so let's do it like this and now what we see is when I execute the function we now see how the function basically or how the product develops then I multiply to that the number 1 from the range here and so 1 times 1 is simply 1 in the next iteration of the loop I'm multiplying 2 on top of that so 1 times 2 becomes 2 so that is why I see the 2 here and in the next iteration I'm going to multiply 3 with it and 2 times 3 will become 6 if I go ahead and let's say if I make this a bit bigger so let's say I want to calculate the factorial of 5 that 1 times 1 is 1 1 times 2 is 2 2 times 3 is 6 6 times 4 is 24 and 24 times 5 gives me 120 so these are the intermediate steps so this is how you can use the print function within a function definition if you have troubles, if you get a wrong result and you want to debug the function then the print function within a function definition may be useful to give you out some intermediate steps but usually when you are done you know your function is correct you will go ahead and remove the line again and then make sure that your result is not printed out but returned and now if we go ahead and quickly copy paste this second version of the factorial function also into Python Tutor and let's also go ahead and call factorial here with 3 and also we have to change the name here to then what we are going to see in comparison to the recursive version also maybe one thing I forgot so the result is actually stored we have to assign it to a variable what we see is in the first step we are going to create the function object in the second step we are going to calculate or we are going to call the function we get a new function scope where n is set to 3 and then within this scope we get a new variable and we assign that 1 to begin with and then we are going to run inside the for loop and inside the for loop the first time we run i is set to 1 so now we multiply product times 1 this is what we do here this gives me a product of 1 of course in the next step in the loop here i becomes 2 and then I am going to multiply the product of 1 times i of 2 which gives me a product of 2 because I am always updating product here and in the next step I become 3 and 3 times 2 will make the product to be 6 and then the 6 is going to be the return value and that means the object, the number 6 is going to be returned and all the intermediate variables here inside the function scope they will be removed, they go away and the result is that we have the same result as in the recursion approach but again in the recursion approach you must not forget you have several function calls in parallel which all have their own scope in the looping version as we saw at all times we only have one function scope so in memory using a looping version is in this case in this scenario easier and as we will see in a future video we don't have unlimited memory of course so the number of recursive function calls is also limited so in some problems we can only solve it realistically for realistic input using the looping approach but for this example for the vectorial function as we saw both approaches yield exactly the same result so let's go back to JupyterLab and let's briefly simplify the function also here a bit so what we can do here is we can go ahead and instead of saying product is product times i what we are going to do is we will simply say product is times equals i and that is the short version that is the update version that is the same as we said product is product times i so we see this will do the same if I execute this here so that is the comparison of the recursion strategy and the looping strategy to solve the vectorial problem depends on how you view the problem if you view it as a looping problem then basically the looping version is more intuitive for you but if you know a little bit about math and you know the definition this recursive definition then of course the recursion logic in code is easier to implement so it really depends on how you personally see the problem this is basically here both approaches are comparable so this is it for this video if you have any questions post them in the comments so I see you in the next video