Implement a Lazy Array
Company: Databricks
Role: Machine Learning Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates understanding of lazy evaluation and deferred computation, implementation of iterator/generator-like abstractions, and the ability to compose functional transformations (map/filter) while managing performance and memory trade-offs.
Constraints
- 0 <= len(nums) <= 100000
- 0 <= len(operations) <= 10000
- -10^9 <= nums[i], x <= 10^9
- For `('take', k)`, 0 <= k <= 100000
- All operations are valid and appear in one of the supported forms
Examples
Input: ([1, 2, 3, 4, 5], [('map', 'mul', 2), ('filter', 'gt', 5)])
Expected Output: [6, 8, 10]
Explanation: Multiply each element by 2 to get [2, 4, 6, 8, 10], then keep values greater than 5.
Input: ([-3, -2, -1, 0, 1, 2, 3], [('filter', 'odd'), ('map', 'add', 1), ('filter', 'ge', 0)])
Expected Output: [0, 2, 4]
Explanation: Odd values are [-3, -1, 1, 3], adding 1 gives [-2, 0, 2, 4], and keeping values >= 0 leaves [0, 2, 4].
Input: ([], [('map', 'add', 5), ('filter', 'even')])
Expected Output: []
Explanation: An empty input remains empty regardless of the lazy operations.
Input: ([1, 2, 3, 4, 5, 6, 7], [('filter', 'even'), ('map', 'mul', 10), ('take', 2)])
Expected Output: [20, 40]
Explanation: Even values are [2, 4, 6], multiplying by 10 gives [20, 40, 60], and taking 2 keeps the first two.
Input: ([2, 2, 2, 3], [('filter', 'eq', 2), ('map', 'add', 1)])
Expected Output: [3, 3, 3]
Explanation: Filter keeps all the 2s, then adding 1 transforms them into 3s.
Input: ([42], [])
Expected Output: [42]
Explanation: With no operations, materializing the lazy array returns the original single element.
Hints
- Do not apply each operation to the whole list right away. Store the operations, and build the iterator pipeline only when `to_list()` or iteration is requested.
- A good laziness test is to use mapping or filtering callables with side effects: increment a counter or raise an exception on access. The counter should stay at 0 until materialization, and with `take(k)` only as many elements as needed to produce `k` outputs should be touched.