Lesson 2 - Understanding Limits
Welcome to Limits
This lesson introduces the single most important idea standing between you and calculus: the limit. A limit captures the value a function approaches as its input gets closer and closer to some point, and it works even when the function has no value at that point at all. That last part sounds strange, but it is exactly what makes limits powerful, and it is the reason derivatives, the engine behind almost all model training, are even possible.
By the end of this lesson, you will be able to:
- Explain in plain language what a limit is and why it differs from simply plugging in a value
- Work through the classic example , where the function is undefined at the point itself
- Approach a limit numerically with NumPy and watch the values converge
- Describe one-sided limits and what it means for a function to be continuous
- Explain why limits are the foundation that derivatives are built on
You should be comfortable with basic Python and NumPy and remember a little high-school algebra (factoring a difference of squares is enough). No prior calculus is needed. Let’s begin.
What Is a Limit?
Most of the functions you have used so far behave politely. If you want to know the value of at , you plug in 1 and get 2. Done. There is nothing subtle happening: the function has a clear value everywhere.
But not every function is that cooperative. Consider this one:
Try to evaluate it at . The numerator becomes , and the denominator becomes . You get , which is undefined. Division by zero has no answer, so the function simply does not exist at . There is a hole in the curve at that exact spot.
And yet, here is the interesting question. What happens near ? If you feed the function values like 0.9, 0.99, 0.999, the results clearly head somewhere specific. They do not blow up or wander; they settle down toward a single number. That number, the value the function is heading toward, is the limit.
A limit is the value a function approaches as its input approaches a particular point. The notation looks like this:
Read aloud, this says: “As approaches 1, the function approaches 2.” Notice what it does not say. It does not say the function equals 2 at , because the function has no value there. It describes the destination the function is traveling toward, not the function’s value at the point itself.
Approaching is not arriving
The whole point of a limit is the word approaches. A limit asks where the function is headed as the input creeps toward a value, completely ignoring what (if anything) happens exactly at that value. A function can have a perfectly well-defined limit at a point where it is itself undefined. That gap between “approaching” and “arriving” is what makes limits useful.
Seeing the Hole
Let’s make this concrete with a picture before touching any code. The function is undefined only at . Everywhere else it behaves like a simple straight line, as you will prove with algebra in a moment. The chart below plots the function and marks the single missing point.
Look closely at . The line clearly wants to pass through the point , but there is an open circle, a hole, marking that the function is not actually defined there. From either side, the curve marches straight toward height 2. The limit fills in the value your eye already expects, even though the function leaves the spot blank.
This is the visual intuition for the entire lesson. A limit answers the question your eye asks automatically: “Where is this curve clearly going?”
Solving the Limit with Algebra
Before we verify anything numerically, it is worth seeing why the limit equals 2. The trick is to rewrite the function into a form that is no longer at the point of interest.
Start with the numerator, . This is a difference of squares, which factors cleanly:
Substitute that back into the function:
Now you can see the source of the trouble and its cure at the same time. For every value of except , the factor is not zero, so you are allowed to cancel it from the top and bottom:
The complicated-looking function is, away from the hole, just the line . That is exactly why the graph looked like a straight line. Now the limit is easy, because is perfectly defined at :
This is the standard strategy for limits that come out to : manipulate the expression algebraically until the problematic factor cancels, then substitute the value directly.
Why the cancellation is legal
Cancelling is only valid because a limit never looks at the point itself, only at values near it. For all those nearby values, is genuinely nonzero, so dividing it out is ordinary algebra. The original function and agree everywhere except at the single hole, and since the limit ignores that one point, both have the same limit of 2.
Approaching the Limit Numerically
Algebra gives you the exact answer, but you can also watch the limit happen by feeding the function values that creep toward 1 from both directions. This is where NumPy is handy. There is no dataset to download for this lesson; you only need NumPy.
import numpy as np
# no dataset needed - numpy only
def f(x):
return (x**2 - 1) / (x - 1)
# Values approaching 1 from the LEFT (smaller than 1)
left = np.array([0.9, 0.99, 0.999, 0.9999])
print("from the left:")
for x in left:
print(f" x = {x:<7} f(x) = {f(x):.4f}")
# Output:
# from the left:
# x = 0.9 f(x) = 1.9000
# x = 0.99 f(x) = 1.9900
# x = 0.999 f(x) = 1.9990
# x = 0.9999 f(x) = 1.9999As climbs toward 1 from below, climbs toward 2. Now approach from the other side, with values slightly larger than 1.
# Values approaching 1 from the RIGHT (larger than 1)
right = np.array([1.1, 1.01, 1.001, 1.0001])
print("from the right:")
for x in right:
print(f" x = {x:<7} f(x) = {f(x):.4f}")
# Output:
# from the right:
# x = 1.1 f(x) = 2.1000
# x = 1.01 f(x) = 2.0100
# x = 1.001 f(x) = 2.0010
# x = 1.0001 f(x) = 2.0001From above, descends toward 2. Both sides agree on the same destination, even though neither side ever lands on it. Squeezed between 1.9999 and 2.0001, the limit can only be 2, which matches the algebra exactly.
Each of these results is just in disguise: , , and so on, because the function equals at every input except 1 itself.
Now try the one value that breaks everything, the point of the hole.
print(f(1.0))
# Output: a division-by-zero warning, and the result nan (not a number)NumPy cannot evaluate the function at exactly . It produces nan (not a number) and warns about dividing by zero, which is precisely the behavior the limit is designed to work around. The function has no value here, but its limit is still a clean, well-defined 2.
Numbers suggest, algebra proves
Plugging in closer and closer values is great for building intuition and checking your work, but it is not a proof. Floating-point arithmetic rounds, and a table of values can occasionally mislead you near tricky points. Treat the numerical approach as confirmation; rely on algebra (or, later, calculus rules) for the actual answer.
One-Sided Limits
You just approached from two directions, and that distinction has a name. A one-sided limit describes the value a function approaches as the input comes in from only one side.
- The left-hand limit uses values smaller than the target and is written with a minus sign:
- The right-hand limit uses values larger than the target and is written with a plus sign:
Here is the rule that ties them together: the ordinary (two-sided) limit exists only when both one-sided limits exist and agree. In our example both sides head to 2, so the full limit is 2. If the two sides disagreed, perhaps the function jumped from one height to another at the point, then the two-sided limit would simply not exist.
This is not a technicality you can ignore. Functions with sudden jumps, steps, and breaks appear all the time, and for those, asking “which side?” is the only way to get a sensible answer.
Limits and Continuity
One-sided limits lead naturally to one of the most useful ideas in calculus: continuity. Informally, a function is continuous at a point if you can draw it through that point without lifting your pen. No holes, no jumps, no gaps.
More precisely, a function is continuous at a point when three conditions all hold:
- is defined (the function actually has a value there).
- The limit exists (both sides agree on a destination).
- The two match: .
Run our example through this checklist at . The limit exists and equals 2, so condition 2 passes. But condition 1 fails: is undefined. Because one condition breaks, the function is not continuous at . That broken point is exactly the hole you saw in the graph.
Interestingly, this is a gentle kind of discontinuity. The limit exists and is finite; the only problem is the missing point. Mathematicians call this a removable discontinuity, because you could “repair” the function simply by defining and patching the hole. The simpler function , which agrees with our function everywhere except the hole, is continuous everywhere, including at .
Why continuity matters later
Continuity is more than vocabulary. Many of the guarantees that machine learning relies on, that a smooth loss surface has a well-defined slope, that small changes in inputs produce small changes in outputs, depend on functions being continuous. When a function is continuous, computing a limit is as easy as substituting the value, which is why continuous functions are so pleasant to optimize.
Why Limits Underpin Derivatives
Everything so far has been building toward the reason limits matter for machine learning. The headline is this: a derivative, the slope of a function at a single point, is defined as a limit. You cannot have one without the other.
Here is the problem a derivative solves. The slope between two points on a curve is straightforward: it is the change in height divided by the change in input.
That formula measures the slope of the straight line connecting two points that are a distance apart. But you do not want the slope across a gap. You want the slope at a single point, the instantaneous steepness right where you are standing. The natural move is to shrink the gap toward zero, bringing the second point closer and closer to the first.
The trouble is that setting outright gives you , the very same undefined expression you met at the start of this lesson. You cannot plug in zero directly. But you can take a limit as approaches zero:
This is the definition of the derivative, and it is a limit through and through. It uses the exact same idea you practiced here: approach a forbidden point () and ask where the expression is heading, rather than what it equals at the point itself. The form is not a dead end; it is an invitation to take a limit.
Because nearly every machine learning model is trained by following the slope of a loss function downhill (a process called gradient descent), and because those slopes are derivatives, and because derivatives are limits, this small idea sits at the foundation of the entire field. You will build the derivative formally in the next lesson, standing on the limits you understand now.
Practice Exercises
Now it is your turn. Try these before checking the hints.
Exercise 1: Approach a Different Limit
Consider the function , which is undefined at . Using NumPy, evaluate it at the values 1.9, 1.99, 2.01, and 2.1, and decide what appears to be.
import numpy as np
def g(x):
return (x**2 - 4) / (x - 2)
# Your code hereHint
Loop over the array np.array([1.9, 1.99, 2.01, 2.1]) and print g(x) for each. The values will close in on a single number from both sides. (Algebra check: , so away from the hole, and the limit is .)
Exercise 2: Cancel the Factor by Hand
For the same function , factor the numerator, cancel the common factor, and use direct substitution to find exactly. Confirm it matches your numerical answer from Exercise 1.
# This one is pencil-and-paper. Write out the factoring,
# then verify with one line of NumPy at a value very close to 2.Hint
The numerator is a difference of squares: . Cancel the that appears top and bottom (legal because the limit never touches itself), leaving . Substitute to get 4.
Exercise 3: Check Continuity
Decide whether is continuous at , and explain which of the three continuity conditions it fails. Then state how you could patch the function to make it continuous there.
import numpy as np
def g(x):
return (x**2 - 4) / (x - 2)
# Try evaluating exactly at x = 2 and observe what happens.
print(g(2.0))Hint
Evaluating at exactly 2 gives nan with a divide-by-zero warning, so is undefined: condition 1 fails. The limit exists and equals 4 (a removable discontinuity), so you could make continuous by defining , patching the hole.
Summary
Congratulations! You have met the idea that makes calculus, and therefore most of machine learning, possible. Let’s review what you learned.
Key Concepts
What a Limit Is
- A limit is the value a function approaches as its input approaches a point
- A limit describes where a function is heading, not necessarily its value at the point
- A function can have a perfectly good limit at a point where it is itself undefined
The Worked Example
- is undefined at because it gives
- Factoring and cancelling shows away from the hole
- Therefore , confirmed numerically from both sides
One-Sided Limits and Continuity
- A one-sided limit approaches from only the left () or only the right ()
- The two-sided limit exists only when both one-sided limits exist and agree
- A function is continuous at a point when it is defined there, its limit exists, and the two match
- A missing point with an existing limit is a removable discontinuity, a hole you could patch
Why It Matters
- The slope between two points is ; shrinking to zero gives
- The derivative is defined as the limit
- Limits turn the forbidden into a well-defined answer
Why This Matters
Limits look like an abstract piece of math, but they are the quiet machinery underneath everything you will do with models. Every time a model trains, it nudges its parameters in the direction that reduces a loss, and that direction comes from a slope, a derivative. Derivatives are limits. Without the ability to ask “where is this heading as the gap shrinks to nothing?” there would be no gradient descent, no backpropagation, and no learning.
You also saw a habit worth keeping: pair intuition, algebra, and code. The graph showed you where the function was going, the algebra proved it, and NumPy let you watch it converge. Holding all three views at once is exactly how you will reason about the more elaborate functions ahead, where you cannot always factor by hand and have to trust the rules instead.
Next Steps
You now understand what a limit is, how to compute one even where a function is undefined, and why limits are the foundation of derivatives. In the next lesson, you will use this idea to define the derivative properly and use it to find the high and low points of a function, the exact skill optimization depends on.
Continue to Lesson 3 - Derivatives and Finding Extreme Points
Turn the limit into a derivative and use it to locate the maximum and minimum points of a function.
Back to Module Overview
Return to the Math Foundations module overview.
Keep Building Your Skills
You have taken a real step toward the math that powers machine learning. The limit may feel like a small idea, but it is the hinge on which derivatives, gradients, and optimization all turn. Keep the three-way habit you practiced here, picture it, prove it, and check it in code, and the calculus in the coming lessons will feel far less mysterious. Master where functions are heading, and you are ready to ask how fast they get there.