Есть ли механизм для цикла x раз в ES6 (ECMAScript 6) без изменяемых переменных?
Типичным способом цикла x
раз в JavaScript является:
for (var i = 0; i < x; i++)
doStuff(i);
Но я не хочу использовать оператор ++
или вообще иметь какие-либо изменяемые переменные. Итак, есть ли способ, в ES6, перевести x
раз по-другому? Мне нравится механизм Ruby:
x.times do |i|
do_stuff(i)
end
Что-нибудь подобное в JavaScript/ES6? Я мог бы обмануть и создать свой собственный генератор:
function* times(x) {
for (var i = 0; i < x; i++)
yield i;
}
for (var i of times(5)) {
console.log(i);
}
Конечно, я все еще использую i++
. По крайней мере, это вне поля зрения:), но я надеюсь, что в ES6 появится лучший механизм.
Ответы
Ответ 1
ОК!
Нижеприведенный код написан с использованием синтаксиса ES6, но может быть легко написан на ES5 или даже меньше. ES6 не является обязательным для создания "механизма для цикла x раз"
Если вам не нужен итератор в обратном вызове, это самая простая реализация
const times = x => f => {
if (x > 0) {
f()
times (x - 1) (f)
}
}
// use it
times (3) (() => console.log('hi'))
// or define intermediate functions for reuse
let twice = times (2)
// twice the power !
twice (() => console.log('double vision'))
Ответ 2
Использование оператора ES2015 Spread:
[...Array(n)].map()
const res = [...Array(10)].map((_, i) => {
return i * 10;
});
// as a one liner
const res = [...Array(10)].map((_, i) => i * 10);
Или если вам не нужен результат:
[...Array(10)].forEach((_, i) => {
console.log(i);
});
// as a one liner
[...Array(10)].forEach((_, i) => console.log(i));
Или используя оператор ES2015 Array.from:
Array.from(...)
const res = Array.from(Array(10)).map((_, i) => {
return i * 10;
});
// as a one liner
const res = Array.from(Array(10)).map((_, i) => i * 10);
Обратите внимание, что если вам просто нужна повторяющаяся строка, вы можете использовать String.prototype.repeat.
console.log("0".repeat(10))
// 0000000000
Ответ 3
for (let i of Array(100).keys()) {
console.log(i)
}
Ответ 4
Я думаю, что лучшим решением является использование let
:
for (let i=0; i<100; i++) …
Это создаст новую переменную i
для каждой оценки тела и гарантирует, что i
будет изменяться только в выражении приращения в синтаксисе цикла, а не где-либо еще.
Я мог бы обмануть и создать свой собственный генератор. По крайней мере, i++
скрывается:)
Это должно быть достаточно imo. Даже на чистых языках все операции (или, по крайней мере, их переводчики) построены из примитивов, которые используют мутацию. Пока это правильно, я не вижу, что с этим не так.
Вы должны быть в порядке с
function* times(n) {
for (let i = 0; i < x; i++)
yield i;
}
for (const i of times(5))
console.log(i);
Но я не хочу использовать оператор ++
или иметь какие-либо изменяемые переменные вообще.
Тогда ваш единственный выбор - использовать рекурсию. Вы можете определить эту функцию генератора без изменяемого i
:
function* range(i, n) {
if (i >= n) return;
yield i;
return yield* range(i+1, n);
}
times = (n) => range(0, n);
Но это кажется излишним для меня и может иметь проблемы с производительностью (поскольку устранение хвостового вызова недоступно для return yield*
).
Ответ 5
Ответ: 09 декабря 2015
Лично я нашел принятый ответ как кратким (хорошим), так и кратким (плохим). Оцените это утверждение, возможно, субъективным, поэтому, пожалуйста, прочитайте этот ответ и посмотрите, согласны ли вы или не согласны
Пример, заданный в вопросе, был чем-то вроде Ruby's:
x.times do |i|
do_stuff(i)
end
Выражение этого в JS, используемом ниже, позволило бы:
times(x)(doStuff(i));
Вот код:
let times = (n) => {
return (f) => {
Array(n).fill().map((_, i) => f(i));
};
};
Что это!
Простой пример использования:
let cheer = () => console.log('Hip hip hooray!');
times(3)(cheer);
//Hip hip hooray!
//Hip hip hooray!
//Hip hip hooray!
В качестве альтернативы, следуя примерам принятого ответа:
let doStuff = (i) => console.log(i, ' hi'),
once = times(1),
twice = times(2),
thrice = times(3);
once(doStuff);
//0 ' hi'
twice(doStuff);
//0 ' hi'
//1 ' hi'
thrice(doStuff);
//0 ' hi'
//1 ' hi'
//2 ' hi'
Сторона примечания - определение функции диапазона
Аналогичный/связанный с этим вопрос, который использует принципиально похожие конструкции кода, может быть, есть удобная функция диапазона в (ядре) JavaScript, что-то похожее на функцию диапазона подчеркивания.
Создать массив с n числами, начиная с x
Подчеркивание
_.range(x, x + n)
ES2015
Пара альтернатив:
Array(n).fill().map((_, i) => x + i)
Array.from(Array(n), (_, i) => x + i)
Демо с использованием n = 10, x = 1:
> Array(10).fill().map((_, i) => i + 1)
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
> Array.from(Array(10), (_, i) => i + 1)
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
В быстром тесте я побежал, каждый из которых выполнял миллион раз каждый с использованием нашего решения и функции doStuff, прежний подход (Array (n).fill()) оказался немного быстрее.
Ответ 6
const times = 4;
new Array(times).fill().map(() => console.log('test'));
Этот фрагмент будет console.log
test
4 раза.
Ответ 7
Я думаю, это довольно просто:
[...Array(3).keys()]
или
Array(3).fill()
Ответ 8
Не то, что я буду преподавать (или когда-либо использовать в своем коде), но здесь достойное codegolf решение без изменения переменной, не нужно ES6:
Array.apply(null, {length: 10}).forEach(function(_, i){
doStuff(i);
})
На самом деле больше интересного доказательства концепции, чем полезный ответ.
Ответ 9
Array(100).fill().map((_,i)=> console.log(i) );
Эта версия удовлетворяет требованиям OP для неизменяемости. Также рассмотрите возможность использования reduce
вместо map
в зависимости от вашего варианта использования.
Это также вариант, если вы не возражаете против небольшой мутации в прототипе.
Number.prototype.times = function(f) {
return Array(this.valueOf()).fill().map((_,i)=>f(i));
};
Теперь мы можем сделать это
((3).times(i=>console.log(i)));
+1 к arcseldon для предложения .fill
.
Ответ 10
Afaik, в ES6 нет механизма, аналогичного методу Ruby times
. Но вы можете избежать мутации, используя рекурсию:
let times = (i, cb, l = i) => {
if (i === 0) return;
cb(l - i);
times(i - 1, cb, l);
}
times(5, i => doStuff(i));
Демо: http://jsbin.com/koyecovano/1/edit?js,console
Ответ 11
Если вы хотите использовать библиотеку, также lodash _.times
или подчеркивание _.times
:
_.times(x, i => {
return doStuff(i)
})
Обратите внимание, что это возвращает массив результатов, поэтому он больше похож на этот рубин:
x.times.map { |i|
doStuff(i)
}
Ответ 12
В функциональной парадигме repeat
обычно есть бесконечная рекурсивная функция. Чтобы использовать его, нам нужен ленивый метод оценки или продолжения прохождения.
Ложное вычисление функции повторения
const repeat = f => x => [x, () => repeat(f) (f(x))];
const take = n => ([x, f]) => n === 0 ? x : take(n - 1) (f());
console.log(
take(8) (repeat(x => x * 2) (1)) // 256
);
Ответ 13
Преимущества этого решения
- Проще всего читать/использовать (imo)
- Возвращаемое значение может использоваться как сумма или просто игнорируется
- Версия Plain es6, также ссылка на TypeScript версия кода
Недостатки
- Мутация. Быть внутренним только мне все равно, может быть, некоторые другие тоже не будут.
Примеры и код
times(5, 3) // 15 (3+3+3+3+3)
times(5, (i) => Math.pow(2,i) ) // 31 (1+2+4+8+16)
times(5, '<br/>') // <br/><br/><br/><br/><br/>
times(3, (i, count) => { // name[0], name[1], name[2]
let n = 'name[' + i + ']'
if (i < count-1)
n += ', '
return n
})
function times(count, callbackOrScalar) {
let type = typeof callbackOrScalar
let sum
if (type === 'number') sum = 0
else if (type === 'string') sum = ''
for (let j = 0; j < count; j++) {
if (type === 'function') {
const callback = callbackOrScalar
const result = callback(j, count)
if (typeof result === 'number' || typeof result === 'string')
sum = sum === undefined ? result : sum + result
}
else if (type === 'number' || type === 'string') {
const scalar = callbackOrScalar
sum = sum === undefined ? scalar : sum + scalar
}
}
return sum
}
Версия TypeScipt
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011
Ответ 14
обращаясь к функциональному аспекту:
function times(n, f) {
var _f = function (f) {
var i;
for (i = 0; i < n; i++) {
f(i);
}
};
return typeof f === 'function' && _f(f) || _f;
}
times(6)(function (v) {
console.log('in parts: ' + v);
});
times(6, function (v) {
console.log('complete: ' + v);
});
Ответ 15
Генераторы? Рекурсия? Почему так много хатина на мутатине?; -)
Если это приемлемо, если мы "спрячем" его, то просто примите использование унарного оператора, и мы можем держать вещи простыми:
Number.prototype.times = function(f) { let n=0 ; while(this.valueOf() > n) f(n++) }
Как и в ruby:
> (3).times(console.log)
0
1
2