Mastering Functional Programming: Recreating map, filter, and reduce in Python
Python's built-in map, filter, and reduce functions are powerful tools for functional programming, enabling concise and expressive data transformations. This challenge asks you to implement these functions from scratch, deepening your understanding of iteration, higher-order functions, and list manipulation.
Problem Description
Your task is to create three Python functions: my_map, my_filter, and my_reduce. These functions should mimic the behavior of Python's built-in map, filter, and functools.reduce respectively.
my_map(func, iterable):
This function should take a function func and an iterable (e.g., a list, tuple) as input. It should apply func to each item in the iterable and return a new iterable containing the results. The order of elements in the output should correspond to the order in the input.
my_filter(func, iterable):
This function should take a function func (which returns a boolean) and an iterable. It should iterate through the iterable and yield only those items for which func returns True. The order of elements in the output should be preserved from the input.
my_reduce(func, iterable, initializer=None):
This function should take a binary function func (that accepts two arguments), an iterable, and an optional initializer. It should apply func cumulatively to the items of the iterable, from left to right, so as to reduce the iterable to a single value.
- If
initializeris provided, it should be used as the starting value. - If
initializeris not provided, the first item of theiterableshould be used as the starting value, and the reduction should begin from the second item. - If the
iterableis empty and noinitializeris provided, aTypeErrorshould be raised.
Examples
Example 1: my_map
Input:
func = lambda x: x * 2
iterable = [1, 2, 3, 4]
Output:
[2, 4, 6, 8]
Explanation:
The lambda function doubles each element in the list.
Example 2: my_filter
Input:
func = lambda x: x % 2 == 0
iterable = [1, 2, 3, 4, 5, 6]
Output:
[2, 4, 6]
Explanation:
The lambda function checks for even numbers. Only elements that satisfy this condition are included in the output.
Example 3: my_reduce (with initializer)
Input:
func = lambda x, y: x + y
iterable = [1, 2, 3, 4]
initializer = 10
Output:
20
Explanation:
The reduction proceeds as follows:
10 + 1 = 11
11 + 2 = 13
13 + 3 = 16
16 + 4 = 20
Example 4: my_reduce (without initializer)
Input:
func = lambda x, y: x * y
iterable = [2, 3, 4]
Output:
24
Explanation:
The reduction proceeds as follows:
2 * 3 = 6
6 * 4 = 24
Example 5: my_reduce (edge case: empty iterable with initializer)
Input:
func = lambda x, y: x + y
iterable = []
initializer = 0
Output:
0
Explanation:
The initializer is returned directly as the iterable is empty.
Example 6: my_reduce (edge case: empty iterable without initializer)
Input:
func = lambda x, y: x + y
iterable = []
Output:
TypeError: reduce() of empty sequence with no initial value
Explanation:
An empty iterable with no initializer should raise a TypeError.
Constraints
- Your implementations of
my_map,my_filter, andmy_reducemust not use Python's built-inmap,filter, orfunctools.reducefunctions. - Your
my_mapandmy_filterfunctions should return iterators (or generator expressions), not lists, to align with the lazy evaluation behavior of the built-ins. - For
my_reduce, ensure correct handling of empty iterables and the optionalinitializer. - The input
iterablecan be any Python iterable (list, tuple, etc.). - The function
funcpassed tomy_mapandmy_filtershould be a callable. - The function
funcpassed tomy_reduceshould be a callable that accepts exactly two arguments.
Notes
- Consider how to handle different types of iterables.
- Think about the use of
yieldformy_mapandmy_filterto create generators. - For
my_reduce, pay close attention to the initialization logic and how to gracefully handle the absence of aninitializer. - Test your functions with various inputs, including empty iterables and iterables with different data types.