Exercises - function composition
Basic Composition
Section titled βBasic CompositionβExercise 1 β π’ Beginner
Implement a compose function that combines two functions right to left, then use it to build a function that negates a doubled number.
# expected:# double = lambda x: x * 2# negate = lambda x: -x
# negate_double = compose(negate, double)# negate_double(5) β -10 β double(5)=10, negate(10)=-10# negate_double(3) β -6Exercise 2 β π’ Beginner
Using compose, build a string transformation that strips whitespace and then capitalises the first letter.
# expected:# clean = compose(str.capitalize, str.strip)# clean(" hello world ") β "Hello world"# clean(" python ") β "Python"Exercise 3 β π‘ Intermediate
Implement compose_all using reduce that composes any number of functions right to left, then verify the order of application.
from functools import reduce
# expected:# add_one = lambda x: x + 1# double = lambda x: x * 2# square = lambda x: x ** 2
# pipeline = compose_all(add_one, double, square)# pipeline(3) β 19 β square(3)=9, double(9)=18, add_one(18)=19# pipeline(2) β 9 β square(2)=4, double(4)=8, add_one(8)=9Pipelines with pipe_all
Section titled βPipelines with pipe_allβExercise 4 β π’ Beginner
Implement pipe_all and use it to build a number processing pipeline that squares a number, then doubles it, then subtracts one.
# expected:# pipeline = pipe_all(# lambda x: x ** 2, # 1. square# lambda x: x * 2, # 2. double# lambda x: x - 1, # 3. subtract one# )
# pipeline(3) β 17 β square(3)=9, double(9)=18, subtract(18)=17# pipeline(4) β 31 β square(4)=16, double(16)=32, subtract(32)=31Exercise 5 β π‘ Intermediate
Use pipe_all to build a name formatting pipeline that cleans and formats a name consistently.
# expected:# format_name = pipe_all(# str.strip,# str.lower,# str.title,# )
# format_name(" ALICE SMITH ") β "Alice Smith"# format_name(" bob JONES ") β "Bob Jones"# format_name("CHARLIE ") β "Charlie"Exercise 6 β π‘ Intermediate
Use pipe_all to build a number validation and transformation pipeline that filters a list of raw string inputs into clean positive integers.
raw = [" 42 ", " -3 ", " 0 ", " 17 ", " -99 ", " 5 "]
# expected pipeline steps:# 1. strip whitespace# 2. convert to int# 3. keep only positive numbers
# expected output: [42, 17, 5]Text Processing Pipelines
Section titled βText Processing PipelinesβExercise 7 β π‘ Intermediate
Build a text sanitization pipeline that prepares user input for storage in a database.
import re
raw_inputs = [ " Alice Smith!! ", " BOB JONES... ", " charlie, brown ",]
# expected pipeline steps:# 1. strip whitespace# 2. lowercase# 3. remove punctuation# 4. normalize multiple spaces into one# 5. title case
# expected output:# ["Alice Smith", "Bob Jones", "Charlie Brown"]Exercise 8 β π‘ Intermediate
Build a pipeline that processes a list of raw email addresses and returns only valid, normalized ones.
import re
emails = [ " Alice@Example.COM ", " invalid-email ", " BOB@GMAIL.COM ", " not_an_email ", " Charlie@Domain.org ",]
# expected pipeline steps:# 1. strip whitespace# 2. lowercase# 3. keep only strings containing "@"
# expected output:# ["alice@example.com", "bob@gmail.com", "charlie@domain.org"]Exercise 9 β π΄ Advanced
Build a log processing pipeline that parses raw log lines and extracts structured data.
raw_logs = [ " [INFO] Server started on port 8080 ", " [ERROR] Connection refused: timeout ", " [WARN] Memory usage above 80% ", " [INFO] Request received: GET /api ", " [ERROR] Database connection failed ",]
# expected pipeline steps:# 1. strip whitespace# 2. parse into {"level": ..., "message": ...}# 3. keep only ERROR level logs# 4. extract just the message
# expected output:# ["Connection refused: timeout", "Database connection failed"]Combined Exercises
Section titled βCombined ExercisesβExercise 10 β π‘ Intermediate
Implement both compose and pipe_all, then show they produce the same result when functions are listed in reverse order.
add_one = lambda x: x + 1double = lambda x: x * 2square = lambda x: x ** 2
# expected β these should produce identical results:# compose_all(add_one, double, square)(3) == pipe_all(square, double, add_one)(3)# both β 19Exercise 11 β π΄ Advanced
Build a reusable data processing framework using pipe_all that applies a pipeline to every item in a list, filters results, and reduces to a final value.
from functools import reduce
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# expected pipeline:# 1. square each number# 2. keep only numbers greater than 20# 3. sum them all with reduce
# expected output: 364 (25 + 36 + 49 + 64 + 81 + 100 + ... wait, recalculate)# 25 + 36 + 49 + 64 + 81 + 100 = 355Exercise 12 β π΄ Advanced
Implement a memoized_pipe β a pipeline where each stepβs results are cached so repeated calls with the same input skip recomputation.
from functools import lru_cache
# expected:# pipeline = memoized_pipe(# lambda x: x ** 2, # step 1 β cached# lambda x: x * 2, # step 2 β cached# lambda x: x - 1, # step 3 β cached# )
# pipeline(5) β 49 (computed)# pipeline(5) β 49 (from cache β no recomputation)# pipeline(3) β 17 (computed)Try building each pipeline from the smallest possible functions β the goal is to write steps so focused that each one does exactly one thing and could be tested in isolation.