Посчитайте суммарную длину имён в списке, возвращая 0 для пустого списка
totalLength должна возвращать суммарное число символов в списке имён. Текущая реализация падает, когда список пуст.
Ограничения: на пустом списке нужно вернуть 0; нельзя добавлять проверку isEmpty() или явный цикл; нельзя строить промежуточный список длин.
fun totalLength(names: List<String>): Int =
names.map { it.length }.reduce { acc, len -> acc + len }
Перепишите тело одним вызовом stdlib и объясните, почему reduce здесь не годится.
У reduce нет затравки: он стартует с первого элемента, на пустой коллекции бросает исключение, а тип аккумулятора равен типу элемента. fold берёт начальное значение, поэтому переживает пустой список и меняет тип: names.fold(0) { acc, n -> acc + n.length }.
- ✗Брать
reduce, когда тип результата отличается от типа элемента - ✗Забывать, что
reduceбросаетUnsupportedOperationExceptionна пустой коллекции - ✗Ставить проверку
isEmpty()вместо того, чтобы задать затравку аккумулятора
- →Когда здесь лучше подойдёт
runningFold, а неfold? - →Как
reduceOrNullменяет поведение на пустой коллекции?
Почему падает reduce
У reduce нет начального значения: он берёт первый элемент как аккумулятор и идёт дальше. На пустой коллекции брать нечего, поэтому он бросает UnsupportedOperationException. Второе ограничение: аккумулятор у reduce того же типа, что и элемент, а нам нужен Int из String.
Решение через fold
fun totalLength(names: List<String>): Int =
names.fold(0) { acc, name -> acc + name.length }
Затравка 0 задаёт тип аккумулятора (Int) и сама становится результатом для пустого списка. Промежуточный список длин не создаётся — map больше не нужен.
Когда reduce уместен
val longest = names.reduce { a, b -> if (a.length >= b.length) a else b }
reduce хорош, когда тип результата совпадает с типом элемента и коллекция гарантированно непуста. Если такой гарантии нет, берите reduceOrNull — он вернёт null вместо исключения.