Composition and Decomposition — Exercise Solutions
1. How to Use This Page
Welcome to the complete exercise solutions for the Composition and Decomposition chapter from the TN State Board Class 11 Computer Science textbook (Samacheer Kalvi). Here is how to get the most out of this page:
- Read the Key Concepts Recap first to brush up on definitions, formulas, and Python examples before attempting the exercises.
- Try each question on your own before reading the solution. Cover the answer with your hand and attempt it honestly.
- Check the explanations — especially for MCQs and True or False — because understanding why an answer is correct matters more than memorising it.
- Use the Common Mistakes and Exam Checklist sections for quick last-minute revision before your exam.
- Bookmark this page and revisit it during revision — repetition builds confidence.
2. Key Concepts (Quick Recap)
2.1 Decomposition
Decomposition is the process of breaking a complex problem into smaller, manageable sub-problems that can be solved independently.
| Aspect | Details |
|---|---|
| Definition | Dividing a large problem into smaller parts |
| Purpose | Simplifies solving, allows parallel work, easier debugging |
| Also called | Problem decomposition, divide-and-conquer |
| Key idea | Each sub-problem should be self-contained and solvable on its own |
# Decomposition example: Student report card
# Instead of writing one massive function, we decompose:
def calculate_total(marks):
return sum(marks)
def calculate_average(total, count):
return total / count
def determine_grade(average):
if average >= 90:
return "A+"
elif average >= 80:
return "A"
elif average >= 70:
return "B"
elif average >= 60:
return "C"
else:
return "D"
def generate_report(name, marks):
total = calculate_total(marks)
average = calculate_average(total, len(marks))
grade = determine_grade(average)
return f"Student: {name}, Total: {total}, Average: {average:.1f}, Grade: {grade}"
2.2 Composition
Composition is the process of combining two or more simple functions to build a more complex function. The output of one function becomes the input of the next.
| Aspect | Details |
|---|---|
| Definition | Combining functions so that output of one is input of another |
| Notation | f ∘ g (read as "f composed with g") |
| Formula | (f ∘ g)(x) = f(g(x)) |
| Key idea | Build complex behaviour from simple, reusable pieces |
# Composition example: combining functions
def add_two(x):
return x + 2
def multiply_by_three(x):
return x * 3
# Composition: multiply_by_three(add_two(x))
# This is the same as (x + 2) * 3
def composed(x):
return multiply_by_three(add_two(x))
print(composed(5)) # (5 + 2) * 3 = 21
2.3 Top-Down Design
Top-down design (also called stepwise refinement) starts with the overall problem and progressively breaks it down into smaller sub-problems.
| Step | Description |
|---|---|
| 1 | Identify the main problem |
| 2 | Break it into major sub-problems |
| 3 | Break each sub-problem further |
| 4 | Continue until each piece is simple enough to code directly |
# Top-down design example: Library Management System
# Level 0: Library Management System
# Level 1: Book Management, Member Management, Transaction Management
# Level 2 (under Book Management): Add Book, Search Book, Delete Book
# Level 2 (under Member Management): Register Member, Update Member, Remove Member
# Level 2 (under Transaction): Issue Book, Return Book, Calculate Fine
def add_book(title, author, isbn):
"""Lowest level — directly implementable."""
return {"title": title, "author": author, "isbn": isbn, "available": True}
def search_book(books, isbn):
for book in books:
if book["isbn"] == isbn:
return book
return None
def book_management(books, action, **kwargs):
"""Composed from lower-level functions."""
if action == "add":
books.append(add_book(kwargs["title"], kwargs["author"], kwargs["isbn"]))
elif action == "search":
return search_book(books, kwargs["isbn"])
return books
2.4 Bottom-Up Design
Bottom-up design starts with the smallest, simplest components and combines them into larger structures.
# Bottom-up approach: building a calculator from atomic operations
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
# Compose into higher-level operations
def percentage(part, whole):
return multiply(divide(part, whole), 100)
def average(numbers):
total = sum(numbers)
return divide(total, len(numbers))
2.5 Modular Programming
Modular programming organises code into separate, independent modules, each handling a specific task.
| Benefit | Explanation |
|---|---|
| Reusability | Modules can be reused across programs |
| Maintainability | Changes in one module do not affect others |
| Readability | Each module has a single, clear purpose |
| Testability | Modules can be tested independently |
| Teamwork | Different developers can work on different modules |
# Modular programming example: separate modules for a student system
# module_math.py
def calculate_average(marks):
return sum(marks) / len(marks)
def calculate_percentage(marks, max_marks):
return (sum(marks) / max_marks) * 100
# module_display.py
def display_report(name, average, grade):
print(f"Student: {name}")
print(f"Average: {average:.2f}")
print(f"Grade: {grade}")
# main.py — composes the modules
from module_math import calculate_average
from module_display import display_report
def main():
marks = [85, 90, 78, 92, 88]
avg = calculate_average(marks)
grade = "A" if avg >= 80 else "B"
display_report("Kumar", avg, grade)
main()
2.6 Function Composition Rules
| Rule | Mathematical Form | Python Equivalent |
|---|---|---|
| Associative | (f ∘ g) ∘ h = f ∘ (g ∘ h) | f(g(h(x))) is unambiguous |
| Not commutative | f ∘ g ≠ g ∘ f (generally) | add_two(multiply_by_three(5)) ≠ multiply_by_three(add_two(5)) |
| Identity | id ∘ f = f ∘ id = f | lambda x: x as identity function |
| Order matters | f ∘ g means apply g first, then f | Inner function executes first |
3. I. Choose the Best Answer (MCQs)
Question 1
The process of breaking a complex problem into smaller, manageable sub-problems is called:
(a) Composition (b) Decomposition (c) Abstraction (d) Encapsulation
Question 2
If f(x) = x + 3 and g(x) = x × 2, then (g ∘ f)(4) equals:
(a) 11 (b) 14 (c) 7 (d) 8
Question 3
Which of the following is an advantage of modular programming?
(a) Code becomes longer (b) Modules cannot be reused (c) Easier debugging and testing (d) Programs run slower
Question 4
In top-down design, problem solving begins with:
(a) The smallest sub-problems (b) Individual lines of code (c) The overall problem at the highest level (d) Data structures first
Question 5
Which of the following statements about function composition is TRUE?
(a) f ∘ g is always equal to g ∘ f (b) The order of composition does not matter (c) Composition is associative: (f ∘ g) ∘ h = f ∘ (g ∘ h) (d) Composition requires all functions to have the same number of parameters
Question 6
Which approach starts by building the smallest components first and then combining them?
(a) Top-down design (b) Bottom-up design (c) Waterfall model (d) Spiral model
Question 7
In the composition f(g(h(x))), which function is executed first?
(a) f (b) g (c) h (d) All simultaneously
Question 8
Decomposition helps in problem solving because:
(a) It makes the problem larger (b) It allows sub-problems to be solved independently (c) It eliminates the need for algorithms (d) It combines all problems into one
4. II. Fill in the Blanks
1. The process of breaking a complex problem into smaller sub-problems is called decomposition.
2. The process of combining two or more functions to create a new function is called composition.
3. In the composition f ∘ g, the function g is executed first.
4. Composition is associative but not commutative.
5. The design approach that starts from the overall problem and refines it step by step is called top-down design.
6. The design approach that starts from the smallest components and combines them is called bottom-up design.
7. A module is a self-contained unit of code that performs a specific task.
8. In function composition (f ∘ g)(x), the output of g becomes the input of f.
9. The identity function id(x) satisfies the property: f ∘ id = id ∘ f = f.
10. Decomposition promotes parallel working because different people can solve different sub-problems at the same time.
11. Modular programming improves code reusability because a module written for one program can be used in another.
12. In top-down design, the process of progressively refining a problem into smaller parts is called stepwise refinement.
5. III. True or False
Statement 1
Decomposition and composition are opposite processes.
Statement 2
Function composition is commutative, i.e., f ∘ g = g ∘ f for all functions f and g.
Correction: Function composition is not commutative. In general, f ∘ g ≠ g ∘ f. For example, if f(x) = x + 2 and g(x) = x × 3, then f(g(1)) = 5 but g(f(1)) = 9.
Statement 3
Top-down design is also known as stepwise refinement.
Statement 4
In modular programming, a change in one module always requires changes in all other modules.
Correction: One of the key advantages of modular programming is independence — a change in one module does not necessarily require changes in others, as long as the interface (inputs and outputs) remains the same.
Statement 5
The identity function id(x) = x acts as the neutral element in function composition.
Statement 6
Bottom-up design starts with the main problem and then breaks it down.
Correction: Bottom-up design starts with the smallest components and builds upward. It is top-down design that starts with the main problem and breaks it down.
Statement 7
Decomposition allows multiple people to work on different sub-problems simultaneously.
6. IV. Match the Following
Match the items in Column A with the correct items in Column B:
| # | Column A | # | Column B |
|---|---|---|---|
| 1 | Decomposition | a | Combining functions to build a complex function |
| 2 | Composition | b | Breaking a problem into sub-problems |
| 3 | Top-down design | c | Building from small components to a whole |
| 4 | Bottom-up design | d | Starting from the overall problem and refining |
| 5 | Modular programming | e | Self-contained, independent code units |
| 6 | Identity function | f | id(x) = x, neutral element of composition |
| 7 | Stepwise refinement | g | Progressive detail addition in top-down design |
| Column A | Match | Column B |
|---|---|---|
| 1. Decomposition | → b | Breaking a problem into sub-problems |
| 2. Composition | → a | Combining functions to build a complex function |
| 3. Top-down design | → d | Starting from the overall problem and refining |
| 4. Bottom-up design | → c | Building from small components to a whole |
| 5. Modular programming | → e | Self-contained, independent code units |
| 6. Identity function | → f | id(x) = x, neutral element of composition |
| 7. Stepwise refinement | → g | Progressive detail addition in top-down design |
7. V. Answer Briefly (2-Mark Questions)
Question 1
Define decomposition with an example.
Decomposition is the process of breaking a complex problem into smaller, manageable sub-problems that can be solved independently.
Example: Consider the problem of creating a student report card. We can decompose it into:
- Sub-problem 1: Calculate total marks
- Sub-problem 2: Calculate average
- Sub-problem 3: Determine grade based on average
- Sub-problem 4: Display the report
Each sub-problem can be solved independently and then combined to produce the final report card.
Question 2
Define composition with an example.
Composition is the process of combining two or more functions so that the output of one function becomes the input of another, creating a new function.
Example: If f(x) = x + 5 and g(x) = x × 2, then the composition g ∘ f is:
- (g ∘ f)(x) = g(f(x)) = g(x + 5) = (x + 5) × 2
In Python:
def f(x):
return x + 5
def g(x):
return x * 2
# Composed: g(f(x))
result = g(f(3)) # g(f(3)) = g(8) = 16
Question 3
What is the difference between top-down and bottom-up design?
| Aspect | Top-Down Design | Bottom-Up Design |
|---|---|---|
| Starting point | Overall problem | Smallest components |
| Direction | From general to specific | From specific to general |
| Process | Break down (decompose) | Build up (compose) |
| Advantage | Clear overview first | Reusable components first |
| Risk | May miss low-level details | May lose sight of the big picture |
Top-down design starts with the big picture and refines it step by step. Bottom-up design starts with the smallest building blocks and combines them upward.
Question 4
What is modular programming? List two advantages.
Modular programming is an approach where a program is divided into separate, independent modules, each responsible for a specific task or functionality.
Two advantages: 1. Reusability: A module written for one program can be reused in another program without modification, saving development time. 2. Maintainability: When a bug is found or a change is needed, only the relevant module needs to be modified. Other modules remain unaffected, reducing the risk of introducing new bugs.
Question 5
Show that composition is not commutative using an example.
Composition is not commutative: f ∘ g ≠ g ∘ f in general.
Let f(x) = x + 3 and g(x) = x × 2.
- f ∘ g applied to x = 4:
- (f ∘ g)(4) = f(g(4)) = f(8) = 8 + 3 = 11
- g ∘ f applied to x = 4:
- (g ∘ f)(4) = g(f(4)) = g(7) = 7 × 2 = 14
Since 11 ≠ 14, f ∘ g ≠ g ∘ f. Hence, composition is not commutative.
8. VI. Answer in Detail (5-Mark Questions)
Question 1
Explain the process of decomposition with a suitable example. Discuss its advantages.
Decomposition is the systematic process of breaking a complex problem into smaller, independent sub-problems that are easier to understand, solve, and manage.
Process of Decomposition:
1. Identify the main problem: Clearly define what needs to be solved. 2. Identify major sub-problems: Break the main problem into 2–5 major parts. 3. Break down further: Divide each sub-problem further until each piece is simple enough to be solved directly. 4. Solve each sub-problem: Write code or design algorithms for each piece. 5. Compose the solutions: Combine the solutions of sub-problems to form the complete solution.
Detailed Example — Building a School Management System:
Python Implementation:
# Sub-problem 1: Student Management
def add_student(students, name, roll_no):
students.append({"name": name, "roll_no": roll_no})
return students
def search_student(students, roll_no):
for student in students:
if student["roll_no"] == roll_no:
return student
return None
# Sub-problem 2: Attendance
def mark_attendance(records, roll_no, status):
records.append({"roll_no": roll_no, "status": status})
return records
# Sub-problem 3: Exam Results
def calculate_average(marks):
return sum(marks) / len(marks)
def determine_grade(average):
if average >= 90:
return "A+"
elif average >= 80:
return "A"
elif average >= 70:
return "B"
elif average >= 60:
return "C"
else:
return "D"
# Composed solution
def generate_report_card(student, marks):
avg = calculate_average(marks)
grade = determine_grade(avg)
return {
"name": student["name"],
"roll_no": student["roll_no"],
"average": avg,
"grade": grade
}
Advantages of Decomposition:
1. Simplifies complex problems: Each sub-problem is smaller and easier to understand. 2. Enables parallel work: Different team members can work on different sub-problems simultaneously. 3. Easier debugging: When an error occurs, it can be isolated to a specific sub-problem. 4. Improves code organisation: Code is structured logically with clear responsibilities. 5. Promotes reuse: Sub-problem solutions can be reused in other contexts.
Question 2
Explain function composition with rules and examples. Show how composition is associative but not commutative.
Function Composition is the process of combining two or more functions to create a new function, where the output of one function becomes the input of the next.
Mathematical Definition:
Given two functions f and g, the composition f ∘ g is defined as:
(f ∘ g)(x) = f(g(x))
This means g is applied first, and then f is applied to the result.
Python Implementation:
# Define simple functions
def double(x):
return x * 2
def add_five(x):
return x + 5
def square(x):
return x ** 2
# Compose: double(add_five(x)) = 2(x + 5)
def compose_2(x):
return double(add_five(x))
# Compose: square(double(add_five(x))) = (2(x + 5))^2
def compose_3(x):
return square(double(add_five(x)))
print(compose_2(3)) # double(add_five(3)) = double(8) = 16
print(compose_3(3)) # square(double(add_five(3))) = square(16) = 256
General Composition Function in Python:
def compose(f, g):
"""Return a function that computes f(g(x))."""
return lambda x: f(g(x))
# Usage
h = compose(double, add_five)
print(h(3)) # 16
# Chain multiple compositions
from functools import reduce
def compose_all(*functions):
return reduce(compose, functions)
composed_fn = compose_all(square, double, add_five)
print(composed_fn(3)) # square(double(add_five(3))) = 256
Rule 1: Composition is Associative
(f ∘ g) ∘ h = f ∘ (g ∘ h)
This means the grouping does not matter. Both evaluate to f(g(h(x))).
Proof by example:
Let f(x) = x², g(x) = x + 1, h(x) = x × 2. Evaluate at x = 3.
- (f ∘ g) ∘ h: First compute (f ∘ g)(x) = f(g(x)) = (x + 1)², then apply h first: (f ∘ g)(h(3)) = (6 + 1)² = 49
- f ∘ (g ∘ h): First compute (g ∘ h)(x) = g(h(x)) = 2x + 1, then: f((g ∘ h)(3)) = f(7) = 49
Both give 49 — associative property holds.
Rule 2: Composition is NOT Commutative
f ∘ g ≠ g ∘ f (in general)
Proof by example:
Let f(x) = x + 3 and g(x) = x × 2. Evaluate at x = 5.
- f ∘ g: f(g(5)) = f(10) = 10 + 3 = 13
- g ∘ f: g(f(5)) = g(8) = 8 × 2 = 16
Since 13 ≠ 16, composition is not commutative.
Rule 3: Identity Function
The identity function id(x) = x acts as the neutral element:
f ∘ id = id ∘ f = f
identity = lambda x: x # id(x) = x
# f ∘ id = f
result1 = add_five(identity(5)) # add_five(5) = 10
# id ∘ f = f
result2 = identity(add_five(5)) # identity(10) = 10
Question 3
Compare top-down and bottom-up design approaches. Explain with examples when each is more suitable.
Top-Down Design (also called stepwise refinement) starts with the overall problem and progressively breaks it down into smaller sub-problems until each is simple enough to implement directly.
Bottom-Up Design starts with the smallest, most basic components and combines them into larger structures until the complete system is built.
Detailed Comparison:
| Criterion | Top-Down Design | Bottom-Up Design |
|---|---|---|
| Starting point | The main/overall problem | Smallest functional units |
| Direction | General → Specific | Specific → General |
| Process | Decomposition | Composition |
| Planning | Requires clear understanding of the whole | Can start with partial understanding |
| Testing | Tested at the end (whole system) | Tested incrementally (unit by unit) |
| Risk | May discover late that a sub-problem is infeasible | May build components that do not integrate well |
| Best for | Well-understood problems | Exploratory or research-oriented problems |
Top-Down Design Example — Payroll System:
# Level 0: Main problem
def payroll_system(employees):
"""Generate payroll for all employees."""
results = []
for emp in employees:
gross = calculate_gross_pay(emp)
deductions = calculate_deductions(emp, gross)
net = calculate_net_pay(gross, deductions)
results.append(format_payslip(emp, gross, deductions, net))
return results
# Level 1: Sub-problems
def calculate_gross_pay(emp):
"""Gross = basic + overtime + bonus."""
basic = emp["basic_pay"]
overtime = compute_overtime(emp["hours_worked"], emp["rate"])
bonus = compute_bonus(emp["performance_rating"])
return basic + overtime + bonus
def calculate_deductions(emp, gross):
"""Deductions = tax + insurance + provident_fund."""
tax = compute_tax(gross)
insurance = emp.get("insurance", 0)
pf = compute_provident_fund(emp["basic_pay"])
return tax + insurance + pf
def calculate_net_pay(gross, deductions):
return gross - deductions
# Level 2: Lowest-level functions
def compute_overtime(hours, rate):
regular_hours = min(hours, 40)
overtime_hours = max(hours - 40, 0)
return overtime_hours * rate * 1.5
def compute_tax(gross):
if gross <= 250000:
return 0
elif gross <= 500000:
return (gross - 250000) * 0.05
else:
return 12500 + (gross - 500000) * 0.1
Bottom-Up Design Example — String Utilities Library:
# Start with the smallest utility functions
def reverse_string(s):
return s[::-1]
def capitalize_words(s):
return " ".join(word.capitalize() for word in s.split())
def count_vowels(s):
return sum(1 for ch in s.lower() if ch in "aeiou")
def remove_spaces(s):
return s.replace(" ", "")
# Compose into higher-level utilities
def is_palindrome(s):
cleaned = remove_spaces(s).lower()
return cleaned == reverse_string(cleaned)
def word_statistics(s):
return {
"word_count": len(s.split()),
"vowel_count": count_vowels(s),
"char_count": len(remove_spaces(s))
}
# Compose further into a text analysis tool
def analyze_text(text):
stats = word_statistics(text)
stats["palindrome_check"] = is_palindrome(text)
stats["reversed"] = reverse_string(text)
stats["capitalized"] = capitalize_words(text)
return stats
When to Use Top-Down:
- The overall system requirements are clear from the start.
- The problem has a natural hierarchical structure.
- You need to present a clear architecture to stakeholders early.
When to Use Bottom-Up:
- Requirements are not fully known and may evolve.
- You have reusable components from previous projects.
- You are building libraries or APIs where low-level correctness is paramount.
In Practice: Most real-world projects use a mixed approach — top-down for overall architecture, bottom-up for reusable utility components.
9. Common Mistakes to Avoid
Mistake 1: Confusing Composition Order
Error: Assuming f ∘ g means "apply f first, then g."
Correction: In f ∘ g, the function closest to the input (g) is applied first. f ∘ g(x) = f(g(x)). Think of it as "g feeds into f." The rightmost function executes first.
# WRONG interpretation
# f ∘ g does NOT mean apply f then g
# f(g(x)) — g is applied FIRST
def f(x):
return x + 3
def g(x):
return x * 2
# f ∘ g at x = 4
result = f(g(4)) # f(g(4)) = f(8) = 11 (g applied first)
# NOT g(f(4)) = g(7) = 14
Mistake 2: Assuming Composition is Commutative
Error: Writing f(g(x)) = g(f(x)) without verification.
Correction: Composition is NOT commutative. Always compute f(g(x)) and g(f(x)) separately. They are equal only in special cases (e.g., when both functions are the identity, or when both are powers of x with the same base).
Mistake 3: Confusing Top-Down and Bottom-Up
Error: Saying "top-down design starts with small parts."
Correction: Top-down starts with the big picture (the overall problem) and breaks it down. Bottom-up starts with the small parts and builds up. Remember: Top-down = decompose from the top; Bottom-up = compose from the bottom.
Mistake 4: Not Decomposing Enough
Error: Breaking a problem into only 2 very large sub-problems that are still too complex.
Correction: Continue decomposing until each sub-problem can be solved by a single, straightforward function. If a sub-problem still requires multiple steps or decisions, decompose it further.
Mistake 5: Creating Modules That Are Tightly Coupled
Error: Writing modules that depend heavily on each other's internal details.
Correction: Each module should have a clear, minimal interface (well-defined inputs and outputs). Modules should communicate through their interfaces, not through shared internal state. This ensures that changes in one module do not cascade through the entire program.
# BAD: Tightly coupled
def process_data(data):
# Directly accesses global variable
global threshold
return [x for x in data if x > threshold]
# GOOD: Loosely coupled
def process_data(data, threshold):
return [x for x in data if x > threshold]
Mistake 6: Forgetting the Identity Function in Composition
Error: Not recognising that the identity function id(x) = x plays a special role.
Correction: The identity function is the neutral element for composition, just as 0 is neutral for addition and 1 is neutral for multiplication. Know that f ∘ id = id ∘ f = f for any function f.
Mistake 7: Writing One Monolithic Function Instead of Composing
Error: Putting all logic into a single long function instead of decomposing and composing.
Correction: Break the logic into small, focused functions and compose them. This improves readability, testability, and reusability.
# BAD: Monolithic
def process_student(name, marks):
total = sum(marks)
average = total / len(marks)
if average >= 90:
grade = "A+"
elif average >= 80:
grade = "A"
else:
grade = "B"
return f"{name}: Total={total}, Avg={average}, Grade={grade}"
# GOOD: Decomposed and composed
def calculate_total(marks):
return sum(marks)
def calculate_average(marks):
return calculate_total(marks) / len(marks)
def determine_grade(average):
if average >= 90:
return "A+"
elif average >= 80:
return "A"
return "B"
def process_student(name, marks):
avg = calculate_average(marks)
grade = determine_grade(avg)
return f"{name}: Total={calculate_total(marks)}, Avg={avg}, Grade={grade}"
10. Exam Checklist
Before your exam, make sure you can tick off every item below:
- [ ] Define decomposition — breaking a complex problem into smaller sub-problems.
- [ ] Define composition — combining functions where output of one becomes input of another.
- [ ] Know the notation — f ∘ g means f(g(x)); g is applied first.
- [ ] Composition is associative — (f ∘ g) ∘ h = f ∘ (g ∘ h).
- [ ] Composition is NOT commutative — f ∘ g ≠ g ∘ f (in general). Be ready to prove with an example.
- [ ] Identity function — id(x) = x; f ∘ id = id ∘ f = f.
- [ ] Top-down design — starts from the overall problem, refines downward (stepwise refinement).
- [ ] Bottom-up design — starts from smallest components, combines upward.
- [ ] Modular programming — organising code into independent, reusable modules.
- [ ] Advantages of decomposition — simplification, parallel work, easier debugging, reuse.
- [ ] Advantages of modular programming — reusability, maintainability, readability, independent testing.
- [ ] Compute composed functions — given f(x) and g(x), calculate f(g(x)) and g(f(x)) at a given value.
- [ ] Draw decomposition trees — represent a problem as a tree with sub-problems as branches.
- [ ] Distinguish top-down vs bottom-up — know when each approach is appropriate.
- [ ] Trace composed function calls — show step-by-step evaluation of f(g(h(x))).
Remember: Decomposition and composition are two sides of the same coin. Decomposition helps you understand a problem by breaking it apart; composition helps you build a solution by putting pieces together. Master both, and you can tackle problems of any size!
