Hone logo
Hone
Problems

Implementing Custom Iterators in Python

Iterators are a fundamental concept in Python, enabling efficient traversal of collections without loading the entire dataset into memory at once. This challenge asks you to implement your own iterator class, allowing you to control how a custom data structure is iterated over. Understanding iterators is crucial for writing performant and memory-efficient code, especially when dealing with large datasets.

Problem Description

You are tasked with creating a class called MyIterator that iterates over a sequence of numbers generated by a given function. The MyIterator class should implement the iterator protocol, meaning it must have an __iter__() method that returns the iterator object itself and a __next__() method that returns the next value in the sequence. The sequence will be generated until a specified condition is met.

Key Requirements:

  • __iter__() method: This method should return the MyIterator object itself.
  • __next__() method: This method should:
    • Generate the next value in the sequence using the provided function.
    • Return the generated value.
    • Raise a StopIteration exception when the sequence is exhausted (i.e., the condition is met).
  • Initialization: The MyIterator class should be initialized with a function generator_function and a stopping condition stop_condition. The generator_function takes a single integer argument (the current number) and returns the next number in the sequence. The stop_condition is a function that takes a single integer argument (the current number) and returns True if the iteration should stop, and False otherwise.

Expected Behavior:

When an instance of MyIterator is used in a for loop or with the next() function, it should generate and return values from the sequence until the stop_condition is met. The StopIteration exception should be raised correctly to signal the end of the iteration.

Edge Cases to Consider:

  • The generator_function might return a value that immediately satisfies the stop_condition.
  • The sequence might be empty from the start (the initial value already satisfies the stop_condition).

Examples

Example 1:

def generator(x):
  return x + 1

def stop(x):
  return x >= 5

iterator = MyIterator(generator, stop)

for num in iterator:
  print(num)

Output:

1
2
3
4

Explanation: The generator function increments the input by 1, and the stop function returns True if the input is greater than or equal to 5. The iterator starts at 0, generates 1, 2, 3, and 4, and stops when the next value would be 5.

Example 2:

def generator(x):
  return x * 2

def stop(x):
  return x > 10

iterator = MyIterator(generator, stop)

print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))

Output:

0
0
0
0
0
0
0

Explanation: The generator doubles the input, and the stop condition checks if the input is greater than 10. The iterator starts at 0, and the loop continues until the next value would be greater than 10.

Example 3: (Edge Case)

def generator(x):
  return x + 1

def stop(x):
  return x == 1

iterator = MyIterator(generator, stop)

for num in iterator:
  print(num)

Output:

0

Explanation: The generator increments the input by 1, and the stop condition checks if the input is equal to 1. The iterator starts at 0, generates 1, and stops because the stop condition is met.

Constraints

  • The generator_function and stop_condition will always be valid functions.
  • The initial value for the sequence is always 0.
  • The generator_function will always return an integer.
  • The stop_condition will always return a boolean.
  • The code should be efficient and avoid unnecessary computations.

Notes

  • Remember to implement both the __iter__() and __next__() methods correctly.
  • The StopIteration exception is crucial for signaling the end of the iteration.
  • Consider how the generator_function and stop_condition interact to determine the sequence.
  • Think about how to handle the initial value and the first iteration.
  • The MyIterator class should be reusable with different generator_function and stop_condition arguments.
class MyIterator:
    def __init__(self, generator_function, stop_condition):
        self.generator_function = generator_function
        self.stop_condition = stop_condition
        self.current_value = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.stop_condition(self.current_value):
            raise StopIteration
        else:
            value = self.generator_function(self.current_value)
            self.current_value = value
            return self.current_value
Loading editor...
python