Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
cubes = (math.cube num for num in list) когда вы можете написать cubes = list.map math.cube И получите тот же самый код что и в js. CoffeуScript просто дает вам варианты.Результаты уже не так однозначны. Но разница всё равно в полтора раза в пользу джаваскрипта.По идее вызов функции на каждую итерацию должен быть медленнее простого суммирования, возможно V8 научился хитро оптимизировать, но нужно будет перепроверить.
'use strict';
const MAX = 1000;
const source = new Array(MAX);
for (let i = 0; i < MAX; i++) {
source[i] = i * 2;
}
// Максимально приближеный к нативному
function trueMyMap(array, callback, thisArg) {
const result = [];
const length = array.length;
for (let i = 0; i < length; i++) {
if (i in array) {
result.push(callback.call(thisArg, array[i], i, array));
}
}
return result;
}
// Простой и быстрый (без учета «дырок»)
function simpleMap(array, callback, thisArg) {
const result = [];
const length = array.length;
for (let i = 0; i < length; i++) {
result.push(callback.call(thisArg, array[i], i, array));
}
return result;
}
// Оптимизированный (без учета «дырок»)
function optimizedMap(array, callback, thisArg) {
const length = array.length;
const result = new Array(length);
const argsLength = callback.length;
for (let i = 0; i < length; i++) {
if (thisArg !== void 0) {
result.push(callback.call(thisArg, array[i], i, array));
} else if (argsLength <= 1) {
result.push(callback(array[i]));
} else if (argsLength === 2) {
result.push(callback(array[i], i));
} else {
result.push(callback(array[i], i, array));
}
}
return result;
}
// Хелпер для запуска тестов
function perf(name, tests) {
console.log(name);
Object.keys(tests).forEach(function (name) {
const callback = tests[name];
console.time(' ' + name);
for (let i = 0; i < 1e4; i++) {
callback();
}
console.timeEnd(' ' + name);
});
}
//
// Тесты
//
perf('Array#map(val)', {
'native': function () {
source.map(function (val) {
return val;
});
},
'trueMyMap': function () {
trueMyMap(source, function (val) {
return val;
});
},
'simple': function () {
simpleMap(source, function (val) {
return val;
});
},
'optimized': function () {
optimizedMap(source, function (val) {
return val;
});
}
});
perf('Array#map(val, idx)', {
'native': function () {
source.map(function (val, idx) {
return val * idx;
});
},
'simple': function () {
simpleMap(source, function (val, idx) {
return val * idx;
});
},
'optimized': function () {
optimizedMap(source, function (val, idx) {
return val * idx;
});
}
});
perf('Array#map(val, idx, array)', {
'native': function () {
source.map(function (val, idx, array) {
return val * idx + array.length;
});
},
'simple': function () {
simpleMap(source, function (val, idx, array) {
return val * idx + array.length;
});
},
'optimized': function () {
optimizedMap(source, function (val, idx, array) {
return val * idx + array.length;
});
}
});
perf('Array#map(val, idx, array) + thisArg', {
'native': function () {
source.map(function (val, idx, array) {
return val * idx + array.length * this.foo;
}, {foo: 123});
},
'simple': function () {
simpleMap(source, function (val, idx, array) {
return val * idx + array.length * this.foo;
}, {foo: 123});
},
'optimized': function () {
optimizedMap(source, function (val, idx, array) {
return val * idx + array.length * this.foo;
}, {foo: 123});
}
});
Array#map(val)
native: 791ms
trueMyMap: 836ms
simple: 210ms
optimized: 164ms
Array#map(val, idx)
native: 823ms
simple: 222ms
optimized: 186ms
Array#map(val, idx, array)
native: 770ms
simple: 190ms
optimized: 203ms
Array#map(val, idx, array) + thisArg
native: 768ms
simple: 180ms
optimized: 199mssome = (args...) -> console.log args
var some,
slice = [].slice;
some = function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return console.log(args);
};
for i in (arr[Symbol.iterator] || arr) работает точно так же (Ну почти, там ещё next надо брать) как for of в JS. Ну и наоборот: http://coffeescript.org/#try:for%20i%20of%20%7Ba%3A%201%2C%20b%3A%202%7D%0A%20%20console.log(i) for(i=0;i<array.length;++i);
for (item in array);
console.log i for i in array
console.log i for i of object
array.forEach (i) -> console.log i
Причина проста и прозаична до безобразия. То, что в javascript называется массивами, в других языках программирования называется хэшами, а время доступа по ключу к элементу хэша это не тоже самое, что время доступа к элементу массива по индексу.
var nums = [1, 2, 3];
delete nums[1];
console.log(nums.length);
cubes = list.map math.cube
вам уже написали (хотя я предпочитаю ставить в таких случаях скобки, то есть единственное отличие будет в отсутствии запятой).cubes = (math.cube num for num in list by 2)
Вы ничего не перепутали? В JS массивы как массивы, там только цифровые индексы, и:
Не стоит винить синтаксический сахар в том, что его используют неправильно
list.reduce ((prev, curr) curr + prev), 0
list.reduce(function(prev, curr) {
return curr + prev;
}, 0);
cubes = (math.cube num for num in list)
var cubes = (function() {
var i, len, results;
results = [];
for (i = 0, len = list.length; i < len; i++) {
num = list[i];
results.push(math.cube(num));
}
return results;
})();
Если при написании кода на CoffeeScript действительно надо думать на порядок больше, то на нём лучше не писать. Весь смысл использования языка, отличного от джаваскрипта в том, чтобы облегчить написание кода, а не усложнить его.
Ну и если есть 2 совершенно разных куска кода, которые используются для достижения одной и той же цели и один кусок быстрее другого, то лучше использовать инструмент, который генерит быстрый код.
cubes = list.map(math.cube) — это короче, понятнее и (судя по тому, что вы доказывали в статье) даже быстрее.cubes = list.map(math.cube) — вилидный CoffeeScript, который не быстрее и не медленнее такой же строчки на JS. А то, что вы написали называется высасывание проблемы с пальца.Знаете, а я предпочитаю больше думать и меньше писать
Если бы у вас был шаг не 1, а 2 (как в моем примере выше) — у вас бы код был аналогичной конструкции.
Если есть способ оптимизировать случай с шагом в 1 — надо это сделать.
Эта тема подробно раскрыта в книге Гэрри Тауба Good Calories Bad Calories, а также в книге Дэвида Перлмуттера Grain Brain.
.push вместо создания массива с заранее заданной длинной. В современных js движках массивы как раз таки массивы, а не хэши, если содержимое однотипно. В V8 методы массивов, вы не поверите, написаны на том же самом js и мало отличаются от представленного выше «оптимизированного» выхлопа кофе — тот же самый цикл и получение / установка элементов по индексу. Вот только с ненужной нам проверкой «дырки» в массиве — необходимо по стандарту. Да, и V8 инлайнит функции, но не коллбэки .map и ему подобных методов (хотя может что в последнее время и изменилось).К счастью, я наделён редким даром. Я всегда узнаю зло, и неважно в какие одежды оно вырядилось на этот раз.
CoffeeScript array comprehensions — модно, стильно, медленно