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

6
36950
Добавить в избранное

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


Да, это продолжение той самой горячей статьи про написание кода без if!

О чём это

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

Под циклами мы имеем в виду императивные циклы наподобие 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. Они позволяют совершать очень много крутых приёмов! Читайте о них больше, пробуйте применять на практике.

Хотите получать больше интересных материалов с доставкой?

Подпишитесь на нашу рассылку:

И не беспокойтесь, мы тоже не любим спам. Отписаться можно в любое время.




Комментариев: 6

  1. Судя по всему вышенаписанному, автор данной статьи вообще слышал для чего изначально программисты старались не использовать If и For(это собственно тот же условный переход)… А делали это на языках низкоуровневых ASM и C/С++ для избежания очистки конвеера на процессоре, в результате код увеличивал свое быстродействие в 10 и более раз. (для тех, кто не в теме — читаем книгу Криса Касперского «Техника оптимизации программ. Эффективное использование памяти»)

  2. А где в задаче #1 собственно пример кода с reduce?

    1. Тоже хотел уточнть…

      1. Ошиблись, уже исправили. Спасибо!

  3. Всё так
    но всегда следует помнить что самый обычный for всегда будет работать быстрей (особенно заметно на больших объёмах)
    Для примера
    https://jsperf.com/for-vs-foreach/32

Добавить комментарий