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

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

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



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









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



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




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


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



for (var i = 0; i < 10; i++) {
//...
}

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



for i in [0...10]

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


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



for i in [0..10]

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



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



for (var i = 0; i <= 10; i++) {
	//...
}

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



for (var i = 10; i >= 0; i--) {
	//..
}

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



for i in [-10..0]

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

[0..-3]
#[0, -1, -2, -3]

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



JavaScript:

var factorial = function(n) {
	var result = 1;

	for (i = 1; i <= n; i++) {
		result *= i;
	}
	return result;
};

factorial(5) //120


CoffeeScript:

factorial = (n) ->
	result = 1
	for i in [1..n]
		result *= i
	result

factorial 5 #120

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



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



factorial = (n) ->
	result = 1
	result *= i for i in [1..n]
	result

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



[...]

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



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



'a,b,c'[''...][0]

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



'a,b,c'.slice('')[0]; //a

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



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



[1...]

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



var __slice = Array.prototype.slice;
__slice.call(1);

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

fn = -> [arguments...]
fn [1..3] #0,1,2,3


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

fn = (args...) -> args
fn [1..3] #0,1,2,3

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

[1 + 1...]


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



[1..10][...2] #1,2


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



[1,2,3...]

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

[1, 2].concat(Array.prototype.slice.call(3));

//[1,2,3]

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



list1 = [1,2,3]
list2 = [4,5,6]

[list1, list2 ...] #[1,2,3,4,5,6]


List comprehension


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



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



factorial = (n) ->
	result = 1
	result *= i for i in [1..n]

factorial 5 #1,2,6,24,120

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



(i for i of location)[0...5]
# hash, host, hostname, href, pathname

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



var list = function() {
	var result = [];

	for (var i in location) {
		result.push(i);
	}

	return result;
}().slice(0, 5);

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



foo = (value for i, value of ['a', 'b', 'c'][0...2]) # [ a, b ]

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



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



foo = (value for value in ['a', 'b', 'c'][0...2])

Или так:

['a', 'b', 'c'].filter (value, i) -> i < 2

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

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

['a', 'b', 'c'].filter(value, i)(function() {
  return i < 2;
});

//ReferenceError: value is not defined!

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


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



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


var i, value;

[
	(function() {
		var _ref, _results;
		_ref = ['a', 'b', 'c'].slice(0, 2);
		_results = [];
		for (i in _ref) {
			value = _ref[i];
			_results.push(value);
		}
		return _results;
	})()
];

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


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



['a', 'b', 'c'].filter(function(value, i) {
	return i < 2;
});

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



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



Оператор then


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



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



for i in [1...10]
	i * i

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


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



for i in [1...10] then i * i

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



Оператор by


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



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



alert i for i in [0..10] by 2 #0,2,4,6,8,10

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



for (var i = 2; i <= 10; i += 2) {
	alert(i);
}

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



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



[i for i in 'Hello World' by 3] #H,l,W,l

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



[for i in 'hello world' by 1 then i.toUpperCase()] # H,E,L,L,O, ,W,O,R,L,D

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



Оператор own


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

var object = {
    foo: 1
};

object.constructor.prototype.bar = 1;

console.log('bar' in object); // true
console.log(object.hasOwnProperty('bar')); // false

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

var object = {
    foo: 1
};

object.constructor.prototype.toString = function() {
      return this.foo;
};
 
for (i in object) {
      if (object.hasOwnProperty(i)) {
            console.log(i); //foo
      }
}

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

object.toString() //1

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

object = foo: 1
object.constructor::toString = -> @foo

for own i of object
  console.log i #foo

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

for own key, value of object
  console.log '#{key}, #{value}' #foo, 1


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


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



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



for (var i = 0; i < 10; i++) if (i === 1) break;

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


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



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



for i in [0..10] if i is 1 break # Parse error on line 1: Unexpected 'TERMINATOR'

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



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



for i in [0..10] then if i is 1 break #Parse error on line 1: Unexpected 'POST_IF'

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



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


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



for i in [0..10] then if i is 1 then break

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



for (i = 0; i <= 10; i++) {
	if (i === 1) {
		break;
	}
}

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



if (foo === true) {
	for (i = 0; i <= 10; i++) {
		if (i === 1) {
			break;
		}
	}
}

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



(if i is 1 then break) for i in [0..10] if foo is on

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



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


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



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



if foo is on then for i in [0..10] when i is 1 then break

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



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



alert i for i in [1..10] when i % 2 is 0

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



for (i = 1; i <= 10; i++) {
	if (i % 2 === 0) {
		alert(i);
	}
}

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



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


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



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



var object = {
	foo: 0,
	bar: 1
};

for (var i in object) {
	alert(key + " : " + object[i]); //0 : foo, 1 : bar
}

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


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



value for key, value of {foo: 1, bar: 2}

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



for key, value of {foo: 1, bar: 2}
	if key is 'foo' and value is 1 then break

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



var object = {
	foo: 1,
	bar: 2
};

for (key in object) {
	if (key === 'foo' && object[i] === 1) {
		break;
	}
}

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



(if value is 1 then alert "#{key} : #{value}") for key, value of document

#ELEMENT_NODE : 1,
#DOCUMENT_POSITION_DISCONNECTED : 1

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



Object.keys obj {foo: 1, bar: 2} # foo, bar

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



object =
	foo: 1
	bar: 2

Object.keys(object).map (key) -> object[key]; # 1, 2


Инструкция while


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



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



factorial = (n) ->
	result = 1
	while n then result *= n--
	result

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



factorial = (n) -> !n and 1 or n * factorial n - 1


Инструкция loop


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



loop then break if foo is bar


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



while (true) {
	if (foo === bar) {
		break;
	}
}


Инструкция until


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


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



expr = /foo/g;
alert "#{array[0]} : #{expr.lastIndex}" until (array = expr.exec('foofoo')) is null

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



var array, expr;
expr = /foo*/g;

while ((array = expr.exec('foofoo')) !== null) {
	alert("" + array[0] + " : " + expr.lastIndex);
}

//foo : 3, foo : 6

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



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


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



loop
	#...
	break if foo()


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


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


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



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



[1..3].map (i) -> i * i

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



[1, 2, 3].map(function(i) {
	return i * i;
});

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



['foo', 'bar'].map (value, i) -> "#{value} : #{i}"
#foo : 0, bar : 1

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



var object = new function() {
	return [0].map(function() {
		return this
	});
};

// [Window map]

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



var object = new function() {
	return [0].map(function() {
		return this;
	}, this);
};
// [Object {}]

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



object = new -> [0].map (i) => @

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



var object = new function() {
	var _this = this;

	return [0].map(function() {
		return _this;
		}, this);
};

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



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


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



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


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



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



Рассмотрим пример:



array = [];
i = 2
while i-- then array[i] = do (i) -> -> i

array[0]() #0
array[1]() #1

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



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



var array = [],
i = 2;

while (i--) {
	array[i] = function(i) {
		return function() {
			return i;
		};
	}(i);
}

array[0]() //0
array[1]() //1


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


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

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

list = []

for i in [0..2] 
   for j in [1..2] 
     list.push i

list # [0,0,1,1,2,2]

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

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

list = []
for i in [0..2] then for j in [1..2] then list.push i

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

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

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

list = []

for i in [0..2]
  list.push i
  for j in [1..1]
     list.push i

list #[0,0,1,1,2,2]


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

list = []

for i in [0..2]
  list.push i
  list.push i for j in [1..1]
    
list #[0,1,2,3]


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

jQuery и пр.


Скажу сразу, для CoffeeScript не важно какая JavaScript библиотека у вас используется.



Начнем с самой главной функции jQuery.ready()

.ready():

$ -> @

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


$(function() {
	return this;
});

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



Следующий метод jQuery в нашем списке — .each(), который почти эквивалентен стандартному методу .forEach()

$.each:

$.each [1..3], (i) -> i

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



$.each([1, 2, 3], function(i) {
	return i;
});


ECMASctipt 6


Если вас не интересует будущее развитие стандарта ECMASctipt 6, можете смело пропустить этот раздел

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

К чему это я?

Дело в том, что будущий синтаксис ES6 практически более чем полностью не совместим с сегодняшним CoffeeScript.

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

[value for key, value of [1,2,3]]

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

var key, value;

[
  (function() {
    var _ref, _results;
    _ref = [1, 2, 3];
    _results = [];
    for (key in _ref) {
      value = _ref[key];
      _results.push(value);
    }
    return _results;
  })()
];

//[1, 2, 3]

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

[for i of [1,2,3]]

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

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

[i * 2 for each (i in [1, 2, 3])];
//2,4,6

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

[i * 2 for each (i in [1, 2, 3]) if (i % 2 == 0)];
//2

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

var object = {
	a: 1,
	b: 2
};

var it = Iterator(lang);

var pair = it.next(); //[a, 1]
	pair = it.next(); //[b, 2]

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

var it = Iterator([1,2,3]);
[i * 2 for (i in it)]; //1, 4, 6


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

Ну. И что?
Реклама
Комментарии 72
  • –1
    ДиапАзон.
  • +12
    Имхо, такое малозаметное различие между [0..10] и [0...10] не очень хорошо для «языка», который делался «для упрощения и разработки более качественного кода».
    • +6
      Когда-то из-за точек в фортране падали космические корабли, а сейчас из-за точек в кофе просто не будут работать сайты. Прогресс налицо!

      Ничему люди не учатся.
      • +1
        Есть такие тонкости, но это не принципиальная вещь, которая бы делала coffeescript непригодным для реальных проектов.
        Можно: использовать .slice 0, 10 вместо [0...10],
        использовать разную подсветку синтаксиса в редакторе для [..] и [...],
        написать issue на трекер проекта или сделать pull request,
        сделать свой fork на github

        Эффект «упрощения и разработки более качественного кода» можно почувствовать только после начала самостоятельного использования coffeescript.
        • 0
          Когда руби учил, тоже смущался. Но после 10 минут привыкаешь.
      • +9
        Вы меня простите, но считать точки — это как-то печально. Как по мне, ни разу не читабельней стандартной конструкции.
        • –1
          я в этом мире чего-то не понимаю, видимо. Для меня код на CoffeeScript не будет читабелен никогда.
          • +1
            Если не писать на CoffeeScript, то очевидно он останется не читабельным, но нужно выходить из зоны комфорта, пробовать другие языки. Я не говорю, что нужно бежать и использовать CoffeeScript, но говорить, что он не читабелен, когда его многие используют и он для них удобен, — не профессионально.
            • 0
              я пробую другие языки и платформы, но на этом я не смогу разрабатывать никогда. При чем тут профессионализм — я не понимаю.
              Пусть люди пишут, а мои мозги под это не заточены.
          • 0
            Первое впечатление: язык идеально подходит для того, чтобы кроме разработчика его не смог прочитать никто. Хотя насчёт того, что сам разработчик сможет это читать спустя эдак месяц, у меня сомнения.

            Это новомодная защита от копирования или язык всё-таки? :)
            • +3
              А вы попробуйте на нем писать. Уверен, что уже через пару часов вы измените свое мнение!
            • 0
              Странно. Вот такой вот код генерится, если в цикле for используется переменная:
              Andrey-Cherkashins-MacBook-Pro:~ andoriyu$ nano test.coffee
              Andrey-Cherkashins-MacBook-Pro:~ andoriyu$ coffee -p test.coffee
              (function() {
              var i, n;

              n = 2;

              for (i = 0; 0
              • 0
                Вероятно, вы забыли разделитель между выражением и телом цикла. Покажите как вы его написали.
                • 0
                  не вставилось: gist.github.com/2212226
                  • 0
                    Этот случай относиться к так называемой «перестраховке» и генерации более безопасного выражения. Для того чтобы код получился более компактным нужно явно задать значение (без использование переменной).
                    • +1
                      Я понимю, я просто не слабо удивился когда вместо красивого цикла for, я увидел for с вложенными тернарными операторами,
                      • 0
                        Интересно, что какой-нибудь компилятор/минифаер сделает с этим кодом. Вполне возможно, что доведёт его до вида без тернарных выражений вообще.
              • +1
                Вообще честно сказать посмотрев в сторону CoffeeScript понял одно, что пока не готов его использовать. Пусть он упрощает кодирование и меньше кода получается, но вот то, что он выдает в файл .js мягко говоря, конфузит. Пока буду использовать синтаксис старого доброго JavaScript.
                • 0
                  Приведите пожалуйста пример того, что вас вводит в этот конфуз.
                  • 0
                    Далеко ходить не буду приведу простой примерчик

                    Содержимое CoffeeScript файла

                        alert j for j in [1..5] 
                            alert i for i in [0..2]
                    


                    Выходной javascript скрипт

                    (function() {
                      var i, j, _i, _len, _ref;
                    
                      for (i = 0; i <= 2; i++) {
                        _ref = [1, 2, 3, 4, 5](alert(i));
                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
                          j = _ref[_i];
                          alert(j);
                        }
                      }
                    
                    }).call(this);
                    


                    А я всего лишь хотел чтобы было вот это:

                    for(j=1; j<=5; j++){
                         alert(j);
                         for(i=0; i<=2; i++){
                               alert(i);
                         }
                    }
                    

                    • 0
                      Не знаю версию вашего компилятора, но интерактивная версия на сайте этот пример не откомпилила:
                      > PARSE ERROR ON LINE 2: UNEXPECTED 'INDENT'
                      • 0
                        CoffeeScript 1.2.0
                        • +1
                          У меня на этой версии все прекрасно компилируется. Я бы не стал выкладывать фейк. Не в моем вкусе.
                        • 0
                          Оно то хоть работает?
                          • 0
                            Если Вы имеете ввиду пример, то у меня интересовал результат преобразования из coffee-script в javascript. Результаты не вдохновили. В конце концов я указал лишь свое мнение и не более.
                    • +3
                      for i in [1..5]
                        alert i
                        for i in [0..2]
                          alert i
                      


                      На выходе:
                      var i;
                      
                      for (i = 1; i <= 5; i++) {
                        alert(i);
                        for (i = 0; i <= 2; i++) {
                          alert(i);
                        }
                      }
                      


                      • –1
                        Во-первых Вы ошибку допустили в скрипте. У Вас два раза i используется переменная.
                        Во-вторых я не понимаю почему в офф документации используют именно этот вариант вызова alert

                         alert j for j in [1..5] 
                        


                        и почему он не работает кода вдруг ниже появляется подобная инструкция со сдвигом?
                        • +2
                          Получается что я не могу доверять полностью CoffeeScript. Ибо мне нужно подразумевать, что некие инструкции могут восприниматься не так, как ты того ожидаешь.
                          • +2
                            Постфиксная запись операторов пришла из Perl, по крайней мере он ее популяризовал. Coffee в этом плане ничего от себя не добавляет.

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

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

                            Если нужно писать код в одну строчку, используем либо постфиксную форму:

                            return if smth < 0
                            


                            или префиксную с then

                            if smth < 0 then return
                            


                            Заметьте, что код хорошо читается и похож на обычное предложение.

                            Если вам нужно написать несколько строк в «теле» оператора, либо одну, но длинную или тяжело читаемую, лучше воспользоваться префиксной формой с идентацией:
                            if smth < 0
                               smth = 0
                               foo = bar + 100500 
                               return
                            


                          • 0
                            Потому что в этом случае используется списочное выражение, и вся логика находится в левой части. Поэтому нужен явный перевод строки. В статье есть пример:
                            • –1
                              тогда почему не учитывается перевод строки после первого выражения for, ведь он явный. Странно как-то получается.
                            • 0
                              Потому что в этом случае используется списочное выражение. Поэтому нужно явно использовать перевод строки.
                          • +2
                            Во первых coffeescript это почти такой же javascript, только без {/}/; т.е. вы можете писать на javascript синтаксом, напоминающим питон. Во вторых то, за что я люблю coffeescript — это классы, и наследование. В третьих обращаю ваше внимание что любое действие в coffeescript возвращает последний шаг, к примеру если вы напишете mydiv.onclick = -> alert(123) то в итоге этот код скомпилируется в mydiv.onclick = function() {return alert(123);}; Ну и в четвертых — онлайн компилятор (кнопка try coffeescript на coffeescript.org/ ) иногда помогает анализировать выходной код.

                            Написал в своё время на нём шахматы с использованием canvas. использовал coffee-script.js и type=«text/coffeescript». Так вот пока идёт трансляция и eval, проходит около минуты и в это время браузер просто виснет намертво. Отсюда следует что большой код лучше компилировать в js, а не пользоваться инлайн-ретранслятором
                            • 0
                              Во-вторых то, за что я люблю coffeescript — это классы, и наследование
                              Вы так говорите, как будто в JS нет классов и наследования.
                              • 0
                                Наоборот, в жаваскрипте очень много классов и наследования. Штук пять несовместимых реализаций.
                                • –1
                                  Реализаций чего? Там ровно одна реализация — прототипно-объектная модель. То что вы считаете «реализацией классов и наследования» — всего лишь попытка сделать ООП «как в Си++».
                                  • 0
                                    Буквально в субботу был на мастер-классе (у нас в Казани), было продемонстрировано минимум три реализации.
                                    • 0
                                      Три реализации «объектов как в Си++».
                                • НЛО прилетело и опубликовало эту надпись здесь
                                  • –3
                                    Я как-то не заметил, что их там нет.
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                      • 0
                                        Упаси боже, не путаю, конечно.
                                        • 0
                                          Скажите, в «Пайтоне» есть ООП? Там всё класс-объекты.
                                          • НЛО прилетело и опубликовало эту надпись здесь
                                  • 0
                                    К сожалению с обратной совместимость не все так гладко, особенно когда речь идет о классическом способе создания объектов.
                                    На эту тему в ближайшее время постараюсь написать отдельную статью.
                                  • +1
                                    Спасибо! Хоть уже и использую пару месяцев coffeescript в одном своем проекте, но операция do до сих пор из-под моего внимаения ускользала. А вот сейчас взял и поменял пару мест у себя в коде, где эта операция пришлась ну очень кстати!

                                    Эх, если бы был еще вариент этой операции, который посволял такие вот случаи обрабатывать:

                                    (function(x) {

                                    })(arr.x)


                                    было бы вообще клево
                                    • 0
                                      ((x) ->)(arr.x)
                                      
                                      • 0
                                        У меня сейчас так и делается.
                                        Просто do — весьма изящный способ избавиться от лишних скобок
                                        • 0
                                          Можно сделать так:
                                          do([arr.x]) ->
                                          

                                          • 0
                                            Нельзя

                                            (function(_arg) {
                                            arr.x = _arg[0];
                                            })([arr.x]);


                                            это не совсем то, что хочется…
                                            В общем, придется дальше скобками обходиться…
                                            • 0
                                              Безусловно это извращений способ.

                                              arr = x:1
                                              do ([arr.x]) -> alert _arg #1
                                              

                                    • 0
                                      do (x = arr.x) ->
                                      • 0
                                        На момент написания статьи этой возможности в языке еще не было)
                                        • 0
                                          Ага, в 1.3.1. добавили.
                                      • +1
                                        CoffeScript по отношению к JS — это как Visual Basic по отношению к C#. Вроде больше слов, проще конструкции, весь язык такой… литературный. Но от обилия этого сахара видеть логику кода становится только сложнее.
                                        • 0
                                          Просто к ЖСу уже замыелн глаз и читая код ты не читаешь каждое слово или символ. Мозг уже сходу видит знакомые конструкции и куски кода и хватает тело цикло, а не его описание и т.д. А смотря новый код ты пыатешь прочитать абсолютно все и осмыслить, что затрудняет восприятие его няшности. В 1 проекте сейчас пишу на кофе, смысла использовать чистый жс в новых проектах больше не вижу :)
                                        • +1
                                          Для меня очень полезный пост, так как сам недавно начал использовать CoffeeScript, но примеры в одну строчку вроде:

                                          (if value is 1 then alert "#{key}: #{value}") for key, value of document

                                          мне кажется, могут отбить желание у новичков использовать прекрасный синтаксис CoffeeScript
                                          • 0
                                            К примеру инструкция for...of, сейчас носит более общий характер нежели это нужно:
                                            [value for key, value of [1,2,3]]

                                            На выходе мы получим следующее извращенство:
                                            var key, value;
                                            alert([
                                            


                                            Вот тут немного непонятно, откуда alert спарсился.
                                            • 0
                                              Спасибо, что заметили, alert лишний.
                                            • 0
                                              Добавил раздел про вложенные инструкции!
                                              • 0
                                                Эм, за напоминание о слайсах — спасибо, в отместку — там опечатка есть
                                                [list1, list2 ...]  #[1,2,3,4,5,6] 
                                                

                                                Не, чтобы получить плоский список надо сказать
                                                [list1..., list2...]
                                                
                                                • 0
                                                  Алсо — с места
                                                  [value for i, value of ['a', 'b', 'c'][0...2]] # a, b
                                                  

                                                  и до конца раздела — это вы о чем?
                                                  Во-первых не нужно писать of [arr], нужно in [arr]
                                                  Во вторых — смысла конструкции вообще не понял — может проще сразу было
                                                   ['a', 'b', 'c'][0...2]
                                                  

                                                  не?
                                                  • 0
                                                    Спасибо что обратили на это внимание, должно быть так:

                                                    foo = (value for i, value of ['a', 'b', 'c'][0...2])
                                                    

                                                    Во-первых не нужно писать of [arr], нужно in [arr]

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

                                                    Суть примеров — показать возможности синтаксических конструкций, а не их эффективности
                                                    • 0
                                                      В данном случае, для получение значений массива использовать нужно именно of, а не in.

                                                      ой ли?
                                                      вот две записи, дающие одинаковый результат
                                                      result_in = ( val for val in ['a', 'b', 'c'][0...2])
                                                      result_of = ( val for key, val of ['a', 'b', 'c'][0...2])
                                                      

                                                      Но почему первая — корректна, а вторая — путь на темную сторону?
                                                      Потому что:
                                                      var key, result_in, result_of, val;
                                                      
                                                      result_in = (function() {
                                                        var _i, _len, _ref, _results;
                                                        _ref = ['a', 'b', 'c'].slice(0, 2);
                                                        _results = [];
                                                        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
                                                          val = _ref[_i];
                                                          _results.push(val);
                                                        }
                                                        return _results;
                                                      })();
                                                      
                                                      result_of = (function() {
                                                        var _ref, _results;
                                                        _ref = ['a', 'b', 'c'].slice(0, 2);
                                                        _results = [];
                                                        for (key in _ref) {
                                                          val = _ref[key];
                                                          _results.push(val);
                                                        }
                                                        return _results;
                                                      })();
                                                      

                                                      Мы же не будем спорить о том, насколько неэффективно обращаться с массивом как объектом?
                                                      • 0
                                                        Хм. действительно я пропустил пример с in.
                                                        Спасибо за замечание!

                                                        Мы же не будем спорить о том, насколько неэффективно обращаться с массивом как объектом?

                                                        Вы должны были заметить что я об этом писал ниже под примером!
                                                  • 0
                                                    Алсо —
                                                    alert i for i in [1..10] when i % 2 is 0
                                                    

                                                    ИМХО удачнее будет
                                                    for i in [1..10] when not( i % 2) then console.log i
                                                    
                                                    • 0
                                                      Алсо —
                                                      expr = /foo*/g;
                                                      alert "#{array[0]} : #{expr.lastIndex}" until (array = expr.exec('foofoo')) is null
                                                      

                                                      пример явно не удачный, потому как это же явно
                                                      expr = /foo/g
                                                      console.log "#{array[0]} : #{expr.lastIndex}" while array = expr.exec 'foofoo'
                                                      
                                                      • 0
                                                        Нет, не должно, хотя бы по тому что раздел называется: инструкция until
                                                        • 0
                                                          Пример от этого удачнее не становится.

                                                          counter = 5
                                                          console.log counter-- until counter is 2
                                                          

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

                                                      Самое читаемое