yego.me
💡 Stop wasting time. Read Youtube instead of watch. Download Chrome Extension

Nested function calls | Intro to CS - Python | Khan Academy


5m read
·Nov 10, 2024

Can I call a function from inside another function? Let's trace what happens and explore why we might want to organize our code this way.

When we call a function from the top level of a program, we create a new stack frame and store all our local variables there. When we later return from that function call, we pop off our stack frame and pass our return value back to the caller. If a function calls another function within its function body, we just do the same thing, but we're going one level deeper. When execution reaches that inner function call, the computer pauses what it's doing, loads that function's instructions, and then goes off to execute them.

This creates a new stack frame on top of our existing stack frame. Our existing stack frame stays where it is because we're not done executing that function yet; it hasn't returned. The computer needs to preserve that state so we can go back and finish executing the function later. Any parameters or variables that are a part of this function get stored on that new top stack frame. When we reach this function's return statement, we exit out, pop off that top stack frame, and return back to where that function was called, which in this case was inside another function.

Notice that the top stack frame now, which is the one that's accessible, is the one that has the state for this function call, and then the computer picks up execution of this function where it left off. We can, in theory, go as many levels deep as we want. Every time the computer calls a function, it creates a new stack frame on the very top, and every time the computer returns from a function call, it pops off the top stack frame. That way, each stack frame is keeping track of the variables local to a specific function call. If we need to pass information through that pipeline of function calls, we can just use the function parameters; we can pass it as an argument to the first function call, which can then pass it as an argument to the inner function call.

Okay, but why might we nest a function call in the first place? Let's set the scene. Imagine a video game where players try to sneak into a building without being detected by the guards. Your chance of detection depends on the volume of your footsteps, and the volume of your footsteps depends on how fast you're moving and the environment you're in. You can be super quiet, crouching in the grass, or you can be super loud, running on metal. Now, here's one way to solve that problem: I have one giant function that takes in as parameters the player's movement type and environment, and then it returns out a Boolean that tells us whether the player has been detected. I can now call this function across my game anytime a player is sneaking around.

So what's the problem then? Well, it's true that I technically have a function, but this function body is huge. Within one function, I'm performing several different tasks. First, I calculate the player's footstep volume based on their movement. Then, I adjust that volume based on the environment they're in. Lastly, I compare that volume against a random detection threshold. When a function does a lot of different things, it makes the program harder to read because it's not always obvious from the function name all of the things that that giant function does.

Two, it makes it harder to test. If I pass in as arguments "walk" and "metal," and the function returns true, well, did it work? I don't know what that final value of volume was that it compared against the threshold, so it's hard to say whether that return value true makes sense or if there's some bug hiding in my function body.

And three, it makes the code harder to reuse. If every function performs only one task, then I can mix and match the functionality as I need. So how do we fix this? We just break it down into several smaller functions.

So first, let's separate out this movement task. We want to take in a base volume and movement type and return out the adjusted volume. So, we'll copy this conditional into our function body and return the volume. Then, inside this wrapper is detected function, we can delete this conditional and replace it with a function call.

Notice that this smaller function is very easy to test now because my function is doing one specific task. It's easy for me to gut check that my return values make sense. If I call it with the argument "Crouch," I should get a smaller volume; and if I call it with the argument "run," I should get a bigger volume. Then, I can just do that same thing with the environment conditional. I separate it out into a smaller function that takes in a volume and an environment type, and then returns out the adjusted volume. Then, I can come back here and nest that function call.

Notice that these function names describe the task that they perform, so we don't really need these comments anymore. Our function names are self-documenting now. For the detection check, I could nest a third function call here, but in this case, I think it might actually be better to separate out this functionality entirely. There's really no reason why this volume calculation needs to be tied to this threshold check.

So, I can instead have a wrapper get footstep volume function that takes in the movement and environment, and returns out the volume. That now lets me have a nice reusable, independent is detected function that takes in a volume and returns out a Boolean.

Okay, we've seen how this makes our program easier to test and easier to read. But how about this reusability benefit? If I want to now add a feature where, say, the player's voice can also give them away, I have these functions in place to make that really easy. A voice would also be modified by the environment, so I can reuse my environment function without having to think twice about it. Then, since I decoupled that detection threshold check, I can calculate my voice volume and then conveniently just plug it into the is detected function to build that whole feature. I had to do very little work because I solved most of those problems already.

So, the next time you're writing a program, think about breaking it down into small functions that each perform a specific task. Then, when you need to combine functionality to perform a bigger task, you can always nest those function calls in a wrapper function.

More Articles

View All
2002 Berkshire Hathaway Annual Meeting (Full Version)
Here but a seconder or anybody would like to speak that motion might now work their way over to the microphone in zone one. Could we have a spotlight on where there it is? And that way when we get to that point of the program, if anybody that would like t…
Worked example: p-series | Series | AP Calculus BC | Khan Academy
So we have an infinite series here: one plus one over two to the fifth plus one over three to the fifth, and we just keep on going forever. We could write this as the sum from n equals one to infinity of 1 over n to the 5th power, 1 over n to the 5th powe…
Khanmigo essay feedback demo | Introducing Khanmigo | Khanmigo for students | Khan Academy
Hey, this is Sarah from KH Academy, and I’m going to show you how to use our “Give Feedback on My Academic Essay” activity from Kigo. Like all other Kigo activities, you can get here from your AI activities page under the right section of the menu. When …
Writing equations of perpendicular lines (example 2) | High School Math | Khan Academy
Find the equation of a line perpendicular to this line that passes through the point (2, 8). So this first piece of information, that it’s perpendicular to that line right over there, what does that tell us? Well, if it’s perpendicular to this line, its …
Constructing linear and exponential functions from graph | Algebra II | Khan Academy
The graphs of the linear function ( f(x) = mx + b ) and the exponential function ( g(x) = a \cdot r^x ) where ( r > 0 ) pass through the points ((-1, 9)) and ((1, 1)). So this very clearly is the linear function; it is a line right over here, and this …
There is no axiomatic proof of property rights
Uh, to avoid confusion, I’ll preface this by saying that, um, I’m personally strongly in favor of property rights and their enforcement. So if you’re new to my channel, please bear that in mind. Uh, Stefan Molyneux made a video a while back attempting to…