CoffeeScript: Подробное руководство по циклам

CoffeeScript: Подробное руководство по циклам

Как известно, CoffeeScriptпредлагает несколько иной набор управляющих конструкций, нежели JavaScript.

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

В этой статье я попытаюсь максимально подробно рассказать о принципах работы с циклами в CoffeeScript и тесно связанными с ними управляющими конструкциями.

Весь код сопровождается сравнительными примерами на JavaScript.

Инструкция for-in

Начнем с самого простого цикла for:

В CoffeeScript он будет записан так:

Для определения количества итераций используются диапазоны. В нашем случае, диапазон от 0. 10 означает: выполнить 10 итераций цикла.

Но как быть если требуется задать условие типа i <= 10?

На первый взляд ничего не изменилось, но если присмотреться, то можно заметить, что в диапазоне одной точкой стало меньше.

В итоге, мы получим следующую запись:

Если начальное значение диапазона больше конечного [10..0], то мы получим обратный цикл с инвертированным результатом:

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

А так, можно заполнить массив отрицательными значениями:

Теперь рассмотрим реальную ситуацию, на примере функции которая, вычисляет факториал числа n:

JavaScript: CoffeeScript:

Как видно из примера выше, код на CoffeeScript более компактный и читабельный по сравнению с JavaScript.

Однако и этот код можно немного упростить:

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

Позволю себе немного отступится от темы и упомянуть еще один интересный момент связанный с применением конструкции [. ] (slice).

Иногда к чужом коде можно встретить примерно такую конструкцию:

Что в конечно счете будет означать следующее:

На первый взгляд, отличить диапазоны от слайсов довольно сложно. Основных отличий два:

Во-первых, в слайсах можно пропустить одно крайнее значение

Здесь мне бы хотелось обратить особое внимание на то, что мы получим после трансляции этого выражения:

Это может быть удобно, например, для получения списка аргументов функции:

Хочу заметить, что в CoffeeScript для получения списка аргументов функции есть более безопасный и изящный вариант (splats):

Также допустимо использование арифметических и логических операций:

Во-вторых, перед слайсами допустимо наличие объекта

В-третьих, в слайсах допустимо использование перечислений

В этом примере выполняется простая операция конкатенации:

Более полезный пример:

List comprehension

Наиболее яркой синтаксической конструкцией для работы с объектами в CoffeeScript, являются списочные выражения (List comprehension).

Пример того, как можно получить список всех вычислений факториала от 1 до n:

Теперь давайте рассмотрим более интересный пример и выведем список первых пяти членов объекта location:

На JavaScript этот код выглядел бы так:

Для того чтобы вывести список элементов (не индексов) массива нужно задать еще один параметр:

C одной стороны, списочные выражения представляет собой очень эффективный и компактный способ для работы с объектами. С другой стороны, нужно четко представлять какой код будет получен после трансляции в JavaScript.

К примеру, код выше, который выводит список элементов от 0 до 2, более эффективно можно переписать так:

На этом месте прошу обратить особое внимание на пробел перед между именем метода и открывающей скобкой. Наличие этого пробела обязательно!

Если пропустить пробел, то мы получим следующее:

Теперь, вам наверное интересно узнать почему вариант с методом .filter() оказался наиболее предпочтителен?

Дело в том, что когда мы используем инструкцию for-of, транслятор подставляет более медленный вариант цикла чем требуется, а именно for-in:

Результат трансляции:

Скажем прямо, итоговый код ужасен.

Теперь давайте посмотрим на код полученный при использовании метода filter:

Как видите, мы получили идеальный и эффективный код!

Если вы используете CoffeeScript на сервере, то вам не о чем беспокоится, ели нет, то стоит помнить, что IE9- не поддерживает метод filter. Поэтому вы сами должны позаботиться о его наличии!

Оператор then

Как известно, для интерпретации выражений, парсер CoffeeScript анализирует отступы, переводы строк, символы возврата каретки и пр.

Ниже представлен типичный цикл для возведения чисел от 1 до n в степень двойки:

Для наглядности, мы использовали перевод строки и отступ.

Однако в реальной ситуации, большинство разработчиков предпочтут записать это выражение в одну строчку:

В инструкциях while, if/else, и switch/when оператор then указывает анализатору на разделение выражений.

Оператор by

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

Выведем только четные числа от 2 до 10:

На JavaScript этот код выглядел бы так:

Опрератор by применяется к диапазону элементов, в которых можно установить шаг итерации.

Также мы можем работать не только с числами или элементами массива, но и со строками:

Операторы by и then могут примеятся совместно:

Хотя этот пример немного надуман и в реальной ситуации шаг "в один" слудует упостить, тем не менее совместная работа операторов by-then позволяет писать очень компактный и эффективный код.

Оператор own

В JavaScript довольно часто используется метод .hasOwnProperty(), который в отличии от оператора in не проверяет свойства в цепочке прототипов объекта:

Рассмотрим пример использования метод .hasOwnProperty() в теле цикла for-in:

Несмотря на то, что мы добавили метод .toString() в прототип объекта object, в теле цикла он перечислен не будет. Хотя к нему можно обратиться напрямую:

В CoffeeScript для этих целей предусмотрен специальный оператор own:

Если нужно использовать второй ключ инструкции for-of , то достаточно его указать через запятую, при этом добавлять еще раз оператор own не нужно:

Условные операторы if/else

Сейчас мне бы хотелось обратить внимаение на один очень важный момент, который связан с совместным использованием циклов с операторами if/else.

Иногда в JavaScript приложениях мы можем встретить подобный код:

Не будем обсуждать и тем более осуждать разработчиков, которые так пишут.

Для нас представляет интерес только как корректно записать выражение в CoffeeScript.

Первое что приходит в голову, сделать так:

Прекрасно. однако согласно правилам лексического анализа CoffeeScriptперед инструкцией if будет обнаружено неожидаемое значение терминала, что приведет к ошибке парсинга!

Из предыдущего материала мы помним, что записать выражение в одну строчку мы можем реализовать с помощью оператора then:

Однако и это не помогло, мы снова видим ошибку парсинга.

Давайте попробуем разобраться.

Дело в том, что инструкция if подчиняется тем же правилам, что и другие инструкции, для которых возможно применение оператора then. А именно, для того чтобы наше выражение правильно распарсилось нужно после выражения с if добавить еще раз оператор then:

Таким образом мы получим следующий код:

Иногда бывают ситуации, когда перед циклом нужно проверить выполнение к.л. условия:

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

Обратитие внимание, что то в этом случае мы не стали использовать опрератор then, при этом никаких ошибок парсинга не произошло!

Условный оператор when

Мы уже рассмотрели операторы by и then, настало время поговорить о следующем операторе в нашем списке, а именно об условном операторе when.

И начнем мы пожалуй с коррекции предыдущего примера:

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

Давайте рассмотрим еще один пример, как можно вывести порядок чисел от 1 до 10 по модулю натурального числа n:

После трансляции в JavaScript код:

Как видите использование оператора when дает нам еще больше возможностей для работы с массивами.

Инструкция for-of

Вы уже видели примеры использования инструкции for-of, когда рассматривали списочные выражения. Теперь давайте более подробно познакомимся с инструкцией for-of, которая наряду с for-in позволяет перебирать свойства объекта.

Давайте сразу проведем сравнительную аналогию с инструкцией for-in в JavaScript:

Как видите для получения значения свойств объекта мы использовали следующий синтаксис: object[i].

В CoffeeScript же, все проще, во-первых мы можем получить значение объекта используя списочные выражения:

Во-вторых, для более сложных выражений мы можем применить более разверную нотацию с применением уже знакомых нам операторов:

В JavaScript тот же результат можно получить так:

Еще один пример эффективного использования for-in:

Напомню, что самым эффективным способом получения списка свойств объекта, явлется метод keys():

Для того чтобы получить значения свойств, метод keys() нужно использовать совместно с методом map():

Инструкция while

По мимо инструкций for-of/in в CoffeeScript также реализована инструкция while.

Когда мы рассматривали инструкцию for-in, я обещал показать еще более эффективный способ вычисления фактриала числа n, время как раз подходящее:

На вскидку хочу добавить, что самое элегантное решение вычисления факториала следующее:

Инструкция loop

На этой инструкции мы не будем долго останавливаться, потому что единственное ее назначение это создание бесконечного цикла:

Инструкция until

Инструкция until аналогична инструкуции while, за одиним исключением, что в выражение добавляется отрицание.

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

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

Инструкция do-while

Скажу сразу, что в CoffeeScript отсутствует реализация инструкции do-while. Однко с помощью нехитрых манипуляций эмитировать ее частичное поведение можно с помощью инструкции loop:

Методы массивов (filter, forEach, map и пр.)

Как известно в CoffeeScript доступны абсолютно все те же методы, что и в JavaSctipt.

Разбирать всю эту группу методов нет смысла, рассмотрим лишь общий принцип работы на примере метода map().

Создадим массив из трех элементов и возведем каждый из них в квадрат:

Рассмотрим еще один пример:

Вторым аргументом, метод map принимает контекст вызова:

Как видите this внутри map указывает на Window, чтобы сменить контекст вызова, сделать это нужно явно:

В CoffeeScript для этой цели предназначен специальный оператор =>:

Иными словами используйте эти методы массивов максимально, где это только возможно.

Кроссбраузерную реализация этих методов я разместил на github'e

Реальный пример использования методов map и filter в CoffeeScript, также можно посмотреть в одном из моих проектов на github'e

Инструкция do / Замыкания

Как известно, в JavaScript активно используются замыкания, при этом CoffeeScript тоже не лишает нас этого удовольствия.

Для создания анонимной самозавязывающейся функции в CoffeeScript используется инструкция do, котороая принимает произвольное число аргументов.

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

На JavaScript код выглядел бы так:

Вложенные инструкции

Вложенные инструкции особо ничем не отличаются от других инструкций и подчиняются тем же правилам:

Для примера, заполним массив парными элементами от 1 до 3:

Как видите нет ничего сложного!

Возможно у вас появится желание записать это в одну строчку. Что же, давайте теперь попробуем упростить запись:

PS: лично я бы, не стал использовать такую запись, однако, вы должны знать, что так тоже допустимо писать, ведь рано или поздно вам придется работать с чужим кодом.

А что если нужно добавить перед вторым циклом какое-то выражение?

В качестве примера выведем три пары элементов от 0-3:

Это правильный вариант и особо улучшать здесь нечего. Записать все в одну строчку тоже не получится, потому что перед вторым циклом требуется явная идентация. А вот тело цикла можно записать в короткой нотации.

В третьей строке можно использовать как префиксную так и постфиксную форму записи.

ECMASctipt 6

Как вы знаете, в будущем стандарте ECMASctipt 6 планируется имплементировать генераторы, итераторы и возможно списочные выражения. А Firefox уже сейчас поддерживает большую часть драфтового стандарта. И дело в том, что будущий синтаксис ES6 практически более чем полностью не совместим с сегодняшним CoffeeScript.

К примеру инструкция for. of, сейчас носит более общий характер нежели это нужно:

На выходе мы получим следующее извращенство :

Будущий стандарт дает возможность использовать итерацию через объекты, куда проще:

Здорово, не правда ли?

Также будет доступны генераторы выражений:

Возможным станет и такая запись:

Станут доступными итераторы:

Итераторы также можно применять совместно с генераторами выражений:

С выходом нового стандарта и многие фишки из CoffeScript перестанут быть таковыми, а разработчикам ядра очевидно предстоит очень много работы, чтобы чтобы удержать «сахарные» позиции. Пожелаем им удачи.

📎📎📎📎📎📎📎📎📎📎