Готов ли реализовать аналог функции reduce
Что проверяют
Понимание функций высшего порядка и итераторного протокола, а на деле внимание к краевым случаям: пустой вход, начальное значение, генератор вместо списка. Следом просят добавить initial или типизировать функцию.
Как отвечать
Сначала проговорите контракт functools.reduce(function, iterable[, initial]): левая свёртка, функция вызывается как f(acc, x), при пустом входе без initial выбрасывается TypeError, единственный элемент без initial возвращается без вызова функции. Потом код:
_missing = object()
def my_reduce(func, iterable, initial=_missing):
it = iter(iterable)
if initial is _missing:
try:
acc = next(it)
except StopIteration:
raise TypeError("empty iterable, no initial") from None
else:
acc = initial
for x in it:
acc = func(acc, x)
return accОбъясните решения:
iter()иnext(): работают с любым итерируемым объектом, включая генераторы и файлы, и не копируют данные в память;- сентинел
object():Noneможет быть законным начальным значением, поэтому «не передали» отличают отдельным объектом; from None: скрывает внутреннийStopIterationиз трейсбека.
Для типизации подойдут параметры типа: def my_reduce[T, A](func: Callable[[A, T], A], iterable: Iterable[T], initial: A) -> A, синтаксис с Python 3.12, в старых версиях через TypeVar.
В конце скажите, что в реальном коде reduce нужен редко: есть sum, math.prod, itertools.accumulate, а цикл читается проще.
Где спотыкаются
Проверяют пустоту через if not iterable. Генератор всегда истинен, пустой проходит проверку, и наружу вылетает StopIteration вместо TypeError.
Делают initial=None по умолчанию, и передать None как настоящий старт уже нельзя.
Путают порядок аргументов, вызывая f(x, acc), или берут iterable[0]. Первое ломает вычитание и другие некоммутативные операции, второе не работает на генераторах.