Сравните порядок map→filter и filter→map: при каком условии результат не изменится?
Перестановка map и filter обычно меняет результат. Она безопасна только тогда, когда условие фильтрации эквивалентно до и после преобразования для каждого элемента, а сами замыкания не имеют побочных эффектов.
map сначала изменяет элементы, поэтому последующий filter проверяет уже преобразованные значения. В обратном порядке фильтр работает с исходными значениями.
Функции map и filter появились как выразительный способ описывать преобразование последовательностей без ручного управления индексами и промежуточным состоянием. Такой стиль отделяет операцию над элементом от механизма обхода коллекции.
Композиция этих операций удобна, но порядок вызовов является частью смысла вычисления. Две цепочки с одинаковыми функциями не обязательно эквивалентны, если меняется порядок преобразований.
Представим список цен, где сначала нужно применить скидку, а затем оставить товары дороже определённого порога. Если сначала отфильтровать исходные цены, порог будет применён до скидки, что означает уже другое бизнес-правило.
Неверная перестановка может привести к лишним товарам в результате, пропущенным элементам или изменению типа данных, с которым работает предикат. При замыканиях с побочными эффектами измениться может также наблюдаемое поведение программы, даже если итоговые значения формально совпадают.
Пусть map задаёт преобразование f, а filter проверяет условие P. Порядок map → filter оставляет элементы, для которых истинно P(f(x)), и возвращает f(x). Порядок filter → map оставляет элементы, для которых истинно P(x), и только потом возвращает f(x).
Результаты совпадают, если для каждого исходного элемента условие P(x) имеет тот же результат, что и P(f(x)), либо если предикат после перестановки был корректно изменён. Дополнительно преобразование не должно менять порядок элементов или создавать побочные эффекты, влияющие на результат.
В первом варианте после скидки обе цены становятся меньше порога, поэтому результат пуст. Во втором сначала проходит исходная цена 120, а после скидки результатом становится 96. Это разные бизнес-смыслы, хотя используются те же две операции.
Для обычных коллекций map сохраняет количество элементов и их относительный порядок, а filter может уменьшить количество элементов, сохраняя порядок оставшихся. Если преобразование меняет тип элемента или удаляет информацию, обратная перестановка может быть не только семантически неверной, но и невозможной.
В каталоге нужно показать товары с финальной ценой не ниже 100 рублей после применения скидки. Команда рассмотрела два варианта: сначала фильтровать товары по исходной цене, а затем рассчитывать скидку, либо сначала рассчитывать финальную цену и фильтровать по ней.
Первый вариант проще, но реализует правило «исходная цена не ниже 100» и может показать товар с финальной ценой 80. Второй вариант точнее соответствует требованиям, однако требует явно определить финальную цену до проверки порога.
Был выбран второй вариант: map → filter, поскольку условие относится именно к преобразованному значению. Результат стал соответствовать отображаемой пользователю цене, а порядок операций был закреплён тестом на товаре, пересекающем порог после скидки.
Нет. Нужно учитывать не только конкретное поле, но и саму структуру результата. Если map меняет тип элемента, удаляет информацию или меняет порядок элементов внутри составного значения, эквивалентность может нарушиться. Безопасность перестановки определяется условием P(x) == P(f(x)) для каждого элемента и сохранением требуемого порядка.
Нет, ленивость не делает разные условия фильтрации одинаковыми. Она меняет момент вычисления и способ потребления элементов, но map → filter по-прежнему проверяет преобразованные значения, а filter → map — исходные. Ленивый вариант может снизить число реально выполненных преобразований, однако семантика порядка сохраняется.
Замыкание в map или filter может изменять внешнее состояние, вести журнал или считать вызовы. При перестановке операций изменяется количество и порядок вызовов: фильтр может убрать элементы до того, как до них дойдёт map. Поэтому для рассуждений об эквивалентности лучше использовать чистые замыкания, зависящие только от входного элемента и не изменяющие внешнее состояние.