В пустой коллекции allSatisfy должен вернуть true или false?

В пустой коллекции allSatisfy должен вернуть true или false?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

allSatisfy для пустой коллекции возвращает true. У предиката нет ни одного элемента, нарушающего условие, поэтому универсальное утверждение считается выполненным.

Исторический контекст

Функциональные методы коллекций появились как способ выразить типовые операции над последовательностями декларативно: проверку условия, поиск, преобразование и агрегацию. allSatisfy заменяет ручной цикл с флагом и явно передаёт намерение: все ли элементы соответствуют предикату.

Такой подход уменьшает шаблонный код и позволяет стандартной библиотеке применять естественное досрочное завершение обхода.

Постановка проблемы

При непонимании поведения пустой коллекции разработчик может ошибочно считать её невалидной и ожидать false. Это особенно опасно при проверке входных данных: пустой набор может пройти проверку всех элементов, хотя бизнес-правило требует хотя бы одного элемента.

Важно различать два условия: «все элементы удовлетворяют предикату» и «коллекция непуста, и все элементы удовлетворяют предикату». allSatisfy проверяет только первое.

Подробное решение

allSatisfy последовательно передаёт элементы предикату и немедленно возвращает false, как только получает отрицательный результат. Если обход завершился без единого нарушения, метод возвращает true.

Для пустой коллекции замыкание не вызывается ни разу. Обход сразу завершается успешным результатом: невозможно найти элемент, который нарушает условие. Это называется вакуу́мной истинностью универсального утверждения.

let values: [Int] = [] let result = values.allSatisfy { $0 >= 0 } print(result) // true

Для непустой коллекции сложность зависит от позиции первого неподходящего элемента: в лучшем случае проверяется один элемент, в худшем — вся коллекция. Для Sequence с одноразовым обходом вызов может потребить элементы до первого нарушения или всю последовательность, если предикат истинен для каждого элемента.

Если нужно требовать непустую коллекцию, проверку следует выразить отдельно, например через условие о наличии элементов и allSatisfy. Не стоит полагаться на побочные эффекты предиката: для пустой коллекции они не выполнятся, а для непустой обход может завершиться досрочно.

Ситуация из практики

Сервис проверяет список разрешённых идентификаторов перед отправкой запроса. Вариант только с allSatisfy корректно проверяет, что запрещённых значений нет, но также пропускает пустой список. Это может быть неверно, если запрос обязан содержать хотя бы один идентификатор.

Ручной цикл даёт полный контроль, но требует дополнительного кода и легко допускает ошибку во флаге результата. Проверка через отрицание contains(where:) логически эквивалентна поиску запрещённого элемента, но хуже передаёт намерение, если задача сформулирована как проверка каждого элемента.

Практичное решение — отдельно проверить непустой вход, затем применить allSatisfy. Так сохраняется досрочное завершение при первом недопустимом элементе, а бизнес-ограничение не смешивается с логикой универсальной проверки.

Что кандидаты часто упускают

1. Вызывается ли предикат хотя бы один раз для пустой коллекции?

Нет. У пустой коллекции нет элементов, поэтому замыкание не вызывается. Это означает, что побочные эффекты внутри предиката не произойдут, а результат будет true независимо от тела замыкания.

2. Проверяет ли allSatisfy все элементы непустой коллекции?

Не обязательно. Метод останавливается на первом элементе, для которого предикат возвращает false. Поэтому он обычно эффективнее ручного обхода, который продолжает проверку после обнаружения нарушения.

3. Чем allSatisfy отличается от contains(where:) с точки зрения пустой коллекции?

allSatisfy проверяет универсальное условие и возвращает true для пустой коллекции. contains(where:) ищет хотя бы один подходящий элемент и возвращает false для пустой коллекции.

Для непустых и вообще любых коллекций логически выполняется связь: «все элементы удовлетворяют условию» эквивалентно отсутствию элемента, нарушающего его. Поэтому allSatisfy(predicate) можно выразить как отрицание contains с противоположным предикатом, но первый вариант обычно понятнее по смыслу.