๐ Recursion
Recursion is a technique where a function calls itself to solve a problem.
A recursive function needs two important parts:
๐ Base Case โ Stops the recursion
๐ Recursive Case โ Function calls itself
๐น Basic Example
def countdown(n):
if n == 0: # Base Case
return
print(n)
countdown(n - 1) # Recursive Case
countdown(5)5
4
3
2
1The function keeps calling itself until n becomes 0.
๐งฎ Factorial Using Recursion
Factorial of 5:
5! = 5 ร 4 ร 3 ร 2 ร 1 = 120def factorial(n):
if n == 1: # Base Case
return 1
return n * factorial(n - 1)
print(factorial(5)) #120<div class="infographic-pyrecursion">
<style>
.infographic-pyrecursion {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--line: #ddd6c9;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--ok: #1a7f4e;
--ok-soft: #e3f5ec;
--warn: #b45309;
--warn-soft: #fdf0dc;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', -apple-system, sans-serif;
--mono-font: 'IBM Plex Mono', 'Courier New', monospace;
margin: 0 auto;
box-sizing: border-box;
font-family: var(--body-font);
color: var(--ink);
background: var(--paper);
}
.infographic-pyrecursion[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--line: #2c313d;
--accent: #5b8def;
--accent-soft:#202942;
--ok: #5cd6a0;
--ok-soft: #16342a;
--warn: #e0a458;
--warn-soft: #3a2c14;
}
.infographic-pyrecursion * { box-sizing: border-box; }
.infographic-pyrecursion .ig-inner { padding: 1.75rem clamp(1.25rem, 5vw, 3rem) 2rem; }
.infographic-pyrecursion .ig-title {
font-family: var(--display-font); font-size: clamp(1.4rem, 4.5vw, 1.9rem);
font-weight: 600; text-align: center; margin: 0 0 1.25rem;
}
.infographic-pyrecursion .code-block {
font-family: var(--mono-font); font-size: 0.82rem; line-height: 1.7;
background: var(--paper-alt); border: 1px solid var(--line); border-radius: 6px;
padding: 0.9rem 1rem; white-space: pre; margin-bottom: 1.5rem;
}
.infographic-pyrecursion .code-block .kw { color: var(--accent); }
.infographic-pyrecursion .code-block .base { color: var(--ok); font-weight: 700; }
.infographic-pyrecursion .code-block .rec { color: var(--warn); font-weight: 700; }
.infographic-pyrecursion .code-block .comment { color: var(--ink-soft); }
.infographic-pyrecursion .controls { display: flex; align-items: center; justify-content: center; gap: 0.6rem; flex-wrap: wrap; padding-top: 1.4rem; }
.infographic-pyrecursion .n-wrap { position: relative; display: flex; align-items: center; height: 2.4rem; }
.infographic-pyrecursion .n-tag {
position: absolute; top: -1.3rem; left: 50%; transform: translateX(-50%);
font-size: 0.65rem; font-family: var(--mono-font); color: var(--accent);
background: var(--accent-soft); padding: 0.1rem 0.4rem; border-radius: 4px; white-space: nowrap;
}
.infographic-pyrecursion .n-input {
width: 4rem; height: 2.4rem; font-family: var(--mono-font); font-size: 1rem; font-weight: 700;
text-align: center; border: 2px solid var(--accent); background: var(--accent-soft); color: var(--accent);
border-radius: 6px; outline: none;
}
.infographic-pyrecursion .ctrl-btn {
font: inherit; font-size: 0.82rem; font-weight: 600; border: 1px solid var(--line);
background: var(--paper-alt); color: var(--ink); padding: 0.45rem 1rem;
border-radius: 999px; cursor: pointer;
}
.infographic-pyrecursion .ctrl-btn:hover { border-color: var(--accent); color: var(--accent); }
.infographic-pyrecursion .ctrl-btn.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
.infographic-pyrecursion .stack-wrap { margin-top: 1.75rem; min-height: 15rem; }
.infographic-pyrecursion .frame {
display: flex; align-items: center; gap: 0.6rem;
font-family: var(--mono-font); font-size: 0.8rem;
border: 1px solid var(--line); background: var(--paper-alt);
padding: 0.5rem 0.7rem; border-radius: 6px; margin-bottom: 0.4rem;
opacity: 0; transform: translateY(-8px) scale(0.95);
transition: opacity 0.3s ease, transform 0.3s ease, background 0.3s ease, border-color 0.3s ease;
}
.infographic-pyrecursion .frame.is-in { opacity: 1; transform: translateY(0) scale(1); }
.infographic-pyrecursion .frame.is-active { border-color: var(--accent); box-shadow: 0 3px 8px rgba(0,0,0,0.1); }
.infographic-pyrecursion .frame.is-base { border-color: var(--ok); background: var(--ok-soft); }
.infographic-pyrecursion .frame.is-returned { border-color: var(--ok); background: var(--ok-soft); opacity: 0.55; }
.infographic-pyrecursion .frame-call { font-weight: 700; color: var(--ink); white-space: nowrap; }
.infographic-pyrecursion .frame-status { color: var(--ink-soft); font-size: 0.75rem; }
.infographic-pyrecursion .frame-status strong { color: var(--ok); }
.infographic-pyrecursion .result-line {
text-align: center; font-family: var(--mono-font); font-size: 0.95rem; font-weight: 700;
margin-top: 1rem; padding: 0.6rem; border-radius: 6px;
background: var(--ok-soft); color: var(--ok); border: 1px solid var(--ok);
opacity: 0; transform: scale(0.9); transition: opacity 0.3s ease, transform 0.3s ease;
}
.infographic-pyrecursion .result-line.is-visible { opacity: 1; transform: scale(1); }
.infographic-pyrecursion .caption {
text-align: center; font-size: 0.85rem; color: var(--ink-soft); margin-top: 1rem; min-height: 1.2em;
}
.infographic-pyrecursion .caption strong { color: var(--ink); font-family: var(--mono-font); }
@media (prefers-reduced-motion: reduce) {
.infographic-pyrecursion *, .infographic-pyrecursion *::before, .infographic-pyrecursion *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="ig-inner">
<h2 class="ig-title">How it Works?</h2>
<div class="code-block"><span class="kw">def</span> factorial(n):
<span class="kw">if</span> n <= <span class="base">1</span>:
<span class="kw">return</span> <span class="base">1</span> <span class="comment"># base case</span>
<span class="kw">return</span> <span class="rec">n * factorial(n - 1)</span> <span class="comment"># recursive case</span></div>
<div class="controls">
<div class="n-wrap">
<span class="n-tag">n</span>
<input type="number" class="n-input" id="nInput" value="4" min="1" max="6">
</div>
<button class="ctrl-btn primary" id="playBtn">โถ Play</button>
<button class="ctrl-btn" id="restartBtn">โฒ Restart</button>
</div>
<div class="stack-wrap" id="stackWrap"></div>
<div class="result-line" id="resultLine"></div>
<p class="caption" id="caption">Set n, then press Play to watch the call stack grow and unwind.</p>
</div>
</div>
<script>
(function () {
const wrapper = document.currentScript.closest('.infographic-pyrecursion')
|| document.querySelector('.infographic-pyrecursion');
const bodyAttr = document.body.getAttribute('data-darkmode');
let dark;
if (bodyAttr === 'true') {
dark = true;
} else if (bodyAttr === 'false') {
dark = false;
} else {
const match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) wrapper.setAttribute('data-theme', 'dark');
const nInput = wrapper.querySelector('#nInput');
const playBtn = wrapper.querySelector('#playBtn');
const restartBtn = wrapper.querySelector('#restartBtn');
const stackWrap = wrapper.querySelector('#stackWrap');
const resultLine = wrapper.querySelector('#resultLine');
const caption = wrapper.querySelector('#caption');
let frameEls = [];
let timer = null;
let steps = [];
let idx = -1;
function buildSteps(n) {
steps = [];
// Push phase
for (let i = n; i >= 1; i--) {
const isBase = i === 1;
steps.push({
type: 'push', depth: n - i, n: i, isBase,
status: isBase ? 'n = 1 โ base case, return 1' : `n = ${i}, calls factorial(${i - 1})`
});
}
// Unwind phase
let value = 1;
for (let i = 1; i <= n; i++) {
const depth = n - i;
if (i === 1) {
steps.push({ type: 'unwind', depth, n: i, text: 'returns 1', value: 1 });
} else {
const newValue = i * value;
steps.push({ type: 'unwind', depth, n: i, text: `${i} * ${value} = ${newValue}`, value: newValue });
value = newValue;
}
}
steps.push({ type: 'done', n, value });
}
function reset() {
stackWrap.innerHTML = '';
frameEls = [];
resultLine.classList.remove('is-visible');
resultLine.textContent = '';
}
function applyStep(step) {
if (step.type === 'push') {
const frame = document.createElement('div');
frame.className = 'frame is-active' + (step.isBase ? ' is-base' : '');
frame.style.marginLeft = `${step.depth * 1.1}rem`;
frame.innerHTML = `<span class="frame-call">factorial(${step.n})</span><span class="frame-status">${step.status}</span>`;
stackWrap.appendChild(frame);
frameEls[step.depth] = frame;
requestAnimationFrame(() => frame.classList.add('is-in'));
frameEls.forEach(f => f && f.classList.remove('is-active'));
frame.classList.add('is-active');
caption.innerHTML = step.isBase
? `<strong>n = 1</strong> hits the base case โ time to unwind`
: `<strong>n = ${step.n}</strong> calls <strong>factorial(${step.n - 1})</strong>`;
}
if (step.type === 'unwind') {
const frame = frameEls[step.depth];
frame.classList.remove('is-active');
frame.classList.add('is-returned');
frame.querySelector('.frame-status').innerHTML = `<strong>${step.text}</strong>`;
caption.innerHTML = `<strong>factorial(${step.n})</strong> returns <strong>${step.value}</strong>`;
}
if (step.type === 'done') {
resultLine.textContent = `factorial(${step.n}) = ${step.value}`;
resultLine.classList.add('is-visible');
caption.innerHTML = `Done โ unwound all the way back to <strong>factorial(${step.n})</strong>`;
}
}
function stop() {
clearInterval(timer);
timer = null;
playBtn.textContent = 'โถ Play';
}
function advance() {
idx++;
if (idx >= steps.length) { stop(); return; }
applyStep(steps[idx]);
if (idx >= steps.length - 1) stop();
}
function play() {
if (timer) { stop(); return; }
if (idx >= steps.length - 1 || idx === -1) {
restart(false);
}
playBtn.textContent = 'โธ Pause';
timer = setInterval(advance, 700);
advance();
}
function restart(clearCaption = true) {
stop();
idx = -1;
reset();
const n = Math.max(1, Math.min(6, Number(nInput.value) || 4));
nInput.value = n;
buildSteps(n);
if (clearCaption) caption.textContent = 'Set n, then press Play to watch the call stack grow and unwind.';
}
playBtn.addEventListener('click', play);
restartBtn.addEventListener('click', () => restart(true));
nInput.addEventListener('change', () => restart(true));
restart(true);
})();
</script></div>โ ๏ธ Why is the Base Case Important?
Without a base case, the function keeps calling itself indefinitely.
def example():
example()
example()This eventually causes a RecursionError.
โญ Remember
Every recursive function should have a clear base case to stop the recursion.