⑴
Let me propose that we introduce a new problem-solving technique that we've actually been using already, just not by name. In programming and in mathematics, there's this idea of recursion. And recursion is a description for a function that calls itself. A function that calls itself is recursive. So what do we mean by this ?

Well, we've actually seen this already. Here's the same pseudocode for binary search earlier. And binary search - - bi implying two - - went either left or right or left or right, halving each time. So that was the division and conquering. So this is the same pseudocode. But notice that in my pseudocode earliear, I literally used the keyword search inside of my definition of search. So this is sort of one of those, like, circular definitions. Like if this is a search algorithm, how am I getting away with using the word search in the definition of search ? It's kind of when you get reprimanded for using a word to define a vocabulary word. This is kind of like that because this algorithm is calling itself. This search function is calling itself. Now, normally, if you were to have a function call itself, call itself, call itself, call itself, it would just do that infinitely. And presumably, it would go on forever. Or the program or computer would probably crash because out of memory or something like that - - more on that next week. But there's a key detail, a key characteristic about this algorithm that makes sure that doing the same thing again and again is not crazy. It's actually going to lead us to a solution. Even though I'm using search here or search here, what's happening to the size of the problem for each of these lines ? It's being cut in half. So even though I'm doing the exact same thing algorithmically again and again. I'm doing it on a smaller, smaller, smaller input - - fewer, fewer, fewer doors - - fewer, fewer, fewer people.

And so even though I'm doing it again and again, so long as I have this so-called base case, as we'll call it, that just makes sure if there's no doors - - you're done. I can break out of what would otherwise be an infinite loop of sorts.

All right, so those two lines, we've already kind of seen.

And actually, we saw this in week zero. So this is the pseudocode for the phone book example from week zero. And you might recall that we had these lines of code here. And this is very procedural or iterative where I literally told you go back to line 3. And we did that today when we took attendance, so to speak. With that third algorithm, you went back to step 2 again and again. That's a very iterative loop-based approach. But I could be a little more clever here too in week zero. We just didn't want to get too far ahead of ourselves.

Let me actually change these highlighted lines to be more, succinctly, search left half book, search right half of book.

I can now tighten the code up. So it's kind of a shorter algorithm. But it's the exact same idea. But on week zero, I very methodically told you what I want you to do next. But here, I'm sort of recognizing that, wait a minute, I just spend the past six lines telling you how to search. So go do more of that. You have all of the logic you need. So this too is recursive in the sense that if this is a search algorithm, the search algorithm is using a search algorithm inside of it. But here too, the phone book was getting divided and divided and divided in half again and again. So recursion doesn't just describe mathematical formulas. It doesn't just describe pseudocode or code - - even physical structures.

So here's a screenshot from Super Mario Brothers 1 on the original Nintendo Entertainment system.

Let me go ahead and get rid of some of the distraction like the ground and the mountains there and here, we have a sort of half pyramid, this is an interesting real world physical structure in that you can define it recursively like what is a pyramid of height 4, if you will.

Well, just to be a little difficult, a pyramid of height 4 is really just a pyramid of height 3 plus one more row. Ok, well, what is a pyramid of height 3 ? Well, a pyramid of height 3 is really just a pyramid of height 2 plus one more row. Well, a pyramid of height 2 is really just a pyramid of height 1 plus one more row. Well, what's a pyramid of height 1 ? A single brick on the screen. You can have a case that just says, like, the buck stops here. Stop asking me these questions. I can special case or hand code my answer to the very tiniest of problems.

So let me go back over to VS Code here. And let me propose that we do one old-school iterative example with this - - code iteration.c. And iteration just means to loop again and again. And iteration is a pass through the code. And let me go ahead and whip this up as follows. Let's include the CS50 library. So we have our get_int function. Let's include standard io so that we have printf. Let's go ahead and create main with no command line arguments today. Let's go ahead and create a variable of type int set equal to the return value of get_int asking the user for the height of the pyramid. And then let me assume for the sake of discussion that there is a function now will draw a pyramid of that height. All right, now let's actually implement that function as a helper function, so to speak.

So if I have a function called draw, it's going to clearly take an integer. And I'll call it, maybe, n as input. And it doesn't need to return anything. So I'm going to say that this is a void function. It doesn't return a value. It just has a side effect of printing bricks on the screen. How can I go about doing a pyramid of height n ? well, I'm going to do this one, which looks like this - - for int i gets 0, i is less than, let's say n, which is the height - - i plus plus. That's going to essentially iterate over every row of the pyramid top to bottom. This might fell similar - - reminiscent - - to problem set one. Then in my inner loop, I'm going to do for, int j gets 0, j is less than i plus 1, j plus, plus. And we'll see why this works in a moment. And then let's go ahead and print out a single hash, no new line. But at the very bottom of this row, let's print out just a new line to move the cursor down a line.

Now, I'm not done yet. I need the prototype. So I'm going to copy this, paste it up here with a semicolon. And I think now the code is correct. Because on my outer loop, I'm iterating n times starting at 0. My inner loop - - realize I want to have at least one harsh then two, then three, then four. So I can't start my number of hashes at 0. And that's why I'm doing j all the way up to i plus 1 so that when i is 0. I'm actually still printing one harsh, and then two harshes and then three. Otherwise, I would start with 0, which is not my goal.

Let me go in and do make iteration to compile this - - ./iteration, Enter. The height will be, for instance, 4. And indeed, it's not quite to scale because these are a little more vertical than they are horizontal.

And I can do this even larger. Let's do like 10 of these. And it gets bigger.

Let's do, like, 50 of these and it gets even bigger. It doesn't even fit on the screen. But it works. And that's an iterative approach, 100% correct and similar to what you might have done already for something like week zero or week one. But it turns out if we leverage recursion we can be a little more clever, in fact.

Let me go ahead and do this. And let me propos to implement the exact same program recursively instead. And I'm going to go ahead and change my draw function to work as follows. What is a pyramid of height 4 ? I said it's a pyramid of height 3 plus 1 more row. So let's take that literally. If a pyramid of height n need to be drawn, let's first draw a pyramid of n minus 1. And then how do I go about drawing one more row ? Well, this I can use a bit of iteration. But I don't need a - - doubly-nested loop anymore. I can set i equal to 0, i less than n, i plus plus. And this block of code is simply going to print one simple row of hashes again and again followed by a new line just to move the cursor down.

So notice this line here. And I'll add some comments - - print pyramid of height n minus 1, print one more row. So I'm kind of taking a bite out of the problem by printing one row.

But I'm deferring to, weirdly, myself, to print the rest of the pyramid. So I'm actually going to do this - - code recursion.c. I'm going to paste that same code into this new version, just so we have a different file without breaking the old one.

And now I'm going to do make recursion. All right, interesting. So Clang is yelling at me with this error. All paths through this function will call itself. So Clang is actually smart enough to notice in my code that no matter what, the draw function is going to call the draw function. And the draw function is going to call the draw function. And the draw function is going to call the draw function. Now, to be fair, n, the input to draw, is getting smaller and smaller. But what's going to happen eventually to the input of draw as I've written this code right now ? Remember that integers are signed by default. They can be both positive or 0 or negative. And so here, if I just keep blindly subtracting 1, it's going to do that seemingly forever until I technically underflow. But that's going to be like 2 billion rows of pyramids later.

So I think what I actually need to do is something like this. I need to ask the question, if n equals 0 - - or heck, just to be super safe, let's say if n is less than or equal to 0, just to make sure it never goes negative, let's just go ahead and return without doing anything. So I'm going to comment this as, like, if nothing to draw, well, then don't blindly call draw again. So this is that so-called base case. This is analogous to saying, like, if John Harvard not in phone book or if no lockers or doors left, then just exit or return in this case. So now I'll never call draw a negative number of times or zero number of times. I'll only do it so long as n is positive.
So now if I make recursion again, does compile fine, ./recursion - - let's do the same input for. And I get the eaxct same number of bricks.

Let's go ahead and do it again with maybe 10. That seems to work too.

Let's do it again with 50. That's seems to work too.

And I can go a little crazy. I can do, like, 5000. This is still going to work. It's just not going to fit on my screen. But it is doing a valian attempt to print all of those out. Now, it turns out that could get us in trouble if we start poking around in the - - now, maybe not 5000 but 50000 or 5 million or beyond. But for now, we'll just assume that this is just a slow process at that. But if we go back to the goal, which was to print this thing here, this too is a recursive structure that we've just now translating the ideas of recursive code to. And actually, if you've never discovered this, we would be remiss in not doing this for you. The point of introducing recursion is that, one, it's actually going to be a very powerful problem-solving technique, because honestly, it, one, we've seen in pseudocode already, it kind of tightens up the amount of code or the amount of lines that you need to write to convey an algorithm. And two, it will actually allow us to solve problems in a fundamentally different way by using computer's memory in an interesting way.