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 перестанут быть таковыми, а разработчикам ядра очевидно предстоит очень много работы, чтобы чтобы удержать «сахарные» позиции. Пожелаем им удачи.