Higher-Order Functions
Higher-Order Function: A function that either takes another function as a parameter OR returns a function as a result.
Why Do We Need Them?
Imagine you have a list of numbers and you want to:
- Keep only the even numbers
- Keep only numbers greater than 10
- Keep only numbers divisible by 3
You could write three different functions... or write ONE function that accepts different "rules" as parameters!
Separate what to do (iterate through a list) from how to decide (the specific rule)
Worked Example: Filtering Numbers
Step 1: The Problem
We have a list of numbers: [3, 8, 15, 4, 22, 7, 11]
We want to filter them based on different conditions.
Step 2: Without Higher-Order Functions (Repetitive)
# Filter for even numbers
def filter_even(numbers):
result = []
for num in numbers:
if num % 2 == 0:
result.append(num)
return result
# Filter for numbers > 10
def filter_large(numbers):
result = []
for num in numbers:
if num > 10:
result.append(num)
return result
Notice how we're repeating the same loop structure? Only the condition changes!
Step 3: With Higher-Order Function (Smart)
def filter_numbers(numbers, condition):
"""
Filter numbers based on any condition function.
numbers: list of numbers
condition: a function that returns True/False
"""
result = []
for num in numbers:
if condition(num): # Call the function we received!
result.append(num)
return result
Now we have ONE function that can work with ANY condition!
Step 4: Define Simple Condition Functions
def is_even(n):
return n % 2 == 0
def is_large(n):
return n > 10
def is_small(n):
return n < 10
Step 5: Use It!
numbers = [3, 8, 15, 4, 22, 7, 11]
print(filter_numbers(numbers, is_even)) # [8, 4, 22]
print(filter_numbers(numbers, is_large)) # [15, 22, 11]
print(filter_numbers(numbers, is_small)) # [3, 8, 4, 7]
We pass the function name WITHOUT parentheses: is_even not is_even()
Practice Exercises
Complete this function:
def filter_words(words, condition):
# Your code here
pass
def is_long(word):
return len(word) > 5
def starts_with_a(word):
return word.lower().startswith('a')
# Test it:
words = ["apple", "cat", "banana", "amazing", "dog"]
print(filter_words(words, is_long)) # Should print: ["banana", "amazing"]
print(filter_words(words, starts_with_a)) # Should print: ["apple", "amazing"]
Write a higher-order function that transforms numbers:
def transform_numbers(numbers, transformer):
# Your code here: apply transformer to each number
pass
def double(n):
return n * 2
def square(n):
return n ** 2
# Test it:
nums = [1, 2, 3, 4, 5]
print(transform_numbers(nums, double)) # Should print: [2, 4, 6, 8, 10]
print(transform_numbers(nums, square)) # Should print: [1, 4, 9, 16, 25]
Create a function that grades scores using different grading systems:
def apply_grading(scores, grade_function):
# Your code here
pass
def strict_grade(score):
if score >= 90:
return 'A'
elif score >= 80:
return 'B'
else:
return 'C'
def pass_fail(score):
return 'Pass' if score >= 60 else 'Fail'
# Test it:
scores = [95, 75, 85, 55]
print(apply_grading(scores, strict_grade)) # Should print: ['A', 'C', 'B', 'C']
print(apply_grading(scores, pass_fail)) # Should print: ['Pass', 'Pass', 'Pass', 'Fail']
Conclusion
1. Functions can be passed as parameters (like any other value)
2. The higher-order function provides the structure (loop, collection)
3. The parameter function provides the specific behavior (condition, transformation)
4. This makes code more reusable and flexible
Python has built-in higher-order functions you'll use all the time:
• sorted(items, key=function)
• map(function, items)
• filter(function, items)
Challenge Exercise
Write a higher-order function validate_sequences(sequences, validator) that checks a list of DNA sequences using different validation rules.
Validation functions to create:
is_valid_dna(seq)- checks if sequence contains only A, C, G, Tis_long_enough(seq)- checks if sequence is at least 10 charactershas_start_codon(seq)- checks if sequence starts with "ATG"
sequences = ["ATGCGATCG", "ATGXYZ", "AT", "ATGCCCCCCCCCC"]
# Your solution should work like this:
print(validate_sequences(sequences, is_valid_dna))
# [True, False, True, True]
print(validate_sequences(sequences, is_long_enough))
# [False, False, False, True]