Учитесь писать код без for

 Зачем писать код с for, если можно этого не делать? Аргументируем, почему.

О чём это

В предыдущей статье приводилось несколько задач, которые нужно было решить без условных операторов. В этой, задач будет немного больше, на решение без циклов. Под циклами мы имеем в виду императивные циклы наподобие for, for … in, for … of, while, do … while. Все они работают по одному принципу — императивный стиль выполнения операций. Альтернативой ему является декларативный стиль.

Императивность vs декларативность

Это весьма обширная тема, но в двух словах разницу можно описать так:

  • Императивный стиль говорит, “как”
  • Декларативный стиль показывает, “что”

Какая между ними разница?

Императивный подход представляет последовательность действий. Сделать то-то, затем это, после что-то ещё. Например: пройтись последовательно по списку чисел, прибавить величину каждого к вычисляющейся сумме.

Декларативный подход показывает, что у нас есть и что нам нужно получить. Например: дан список чисел, нужно получить их сумму.

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

К счастью, языки программирования уже предлагают нам средства для декларативного описания императивных инструкций. В этой статье мы сконцентрируемся на декларативном задании циклов.

Сразу скажем, что не нужно искать единственно верный способ программирования. Любая нетривиальная программа почти наверняка должна включать в себя оба подхода. Лучше всего — просто их знать и уверенно пользоваться обоими.

Immutability — неизменяемость

Избегать циклы стоит не только для того, чтобы приобщиться к декларативному стилю. Важный момент здесь — относиться к данным как к immutable.

Неизменяемость данных — ещё одна большая тема, но в глобальном смысле суть — не изменять данные в переменных и свойствах объектов для представления состояния приложения. Вместо того, состояние сохраняется в фазах между вызовами функций. Функции вызывают друг друга последовательно, чтобы превратить первоначально поданные данные в иные формы. Переменные в этом процессе не модифицируются.

Вместо того, чтобы хранить состояние в переменных для выполнения простых операций, сделайте их неизменяемыми: это безопаснее и чище. Код с неизменяемыми переменными намного проще для работы и расширения.

Читаемость и производительность кода могут быть лучше при таком подходе. Как находить баланс — отдельная тема, достойная обсуждения.

Рекурсия

Ещё один способ избежать цикла — это использовать рекурсию.

Рекурсия проста в исполнении. Создаёте функцию, которая вызывает саму себя (создавая тем самым цикл), добавляете условие выхода.

Не факт, что рекурсию можно отнести к декларативному стилю, но это как минимум альтернатива обычному циклу. Также, рекурсия может быть менее производительна, код менее читаемым.

Иногда рекурсия — лучший способ решить задачу, и мы можем обойти её, воспользовавшись стеком (это несложно).

Задачи

В любом случае, написать что-то без циклов — это просто интересный челлендж!

Здесь приведено несколько задач с решениями, использующими императивные циклы и не использующими. Все примеры написаны на JavaScript.

Какие решения вам нравятся больше, какие легче читаются, как по-вашему?

Задача №1: вычислить сумму чисел в массиве

Предположим, у нас есть массив чисел наподобие следующего:

Здесь мы для достижения результата постоянно изменяем переменную sum.

Вот решение, использующее прекрасную функцию reduce:

Промежуточные состояния нигде не перезаписываются. Вместо этого, мы сделали множество вызовов функции, а состояние передавалось между этими вызовами до конечного присваивания к sum.

А вот решение с применением рекурсии:

Функция sum вызывает саму себя и использует оператор rest, чтобы уменьшить суммируемый массив. И останавливается, когда массив пуст.

Кому-то может показаться это решение хорошим, но как минимум, оно хуже читается, нежели решение с reduce.

Задача №2: составить предложение из смешанных данных

Допустим, дан массив из string и объектов прочих типов, и нам необходимо склеить все string, игнорируя остальные элементы.

Пример для тестирования:

Ожидаемый вывод — “Hello!”. Для проверки на тип string, стоит использовать оператор typeof.

Решение с применением простого цикла:

А вот решение, использующее функцию filter в комбинации с join:

Берите эти функции на заметку! filter изящно скрывает за собой условные операции, и мы получаем новый уровень абстракции.

Задача №3: сделать список объектов из списка значений

Допустим, у нас есть массив из названий книг. Нам необходимо сделать из каждого объект и дать ему уникальный идентификатор.

Пример данных:

Решение с применением обычного цикла:

Решение с простым использованием функции map:

В итоге

Все решения, предложенные здесь, основаны на функциях map, filter и reduce. Они позволяют совершать очень много крутых приёмов! Читайте о них больше, пробуйте применять на практике.

Источник.