在JavaScript中循环x次的典型方法是:
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中有更好的机制。
在函数式范式中,重复通常是一个无限递归函数。要使用它,我们需要惰性求值或延续传递样式。
惰性求值函数重复
const重复= f = > x = > [x,() = >重复(f) (f (x)));
Const take = n => ([x, f]) => n === 0 ?X:取(n - 1) (f());
console.log (
取(8)(repeat(x => x * 2) (1)) // 256
);
我使用一个thunk(一个没有参数的函数)来实现Javascript中的惰性求值。
延续传递样式的函数重复
const重复= f = > x = > [x, k = > k(重复(f) (f (x))));
Const take = n => ([x, k]) => n == 0 ?X: k(取(n - 1));
console.log (
取(8)(repeat(x => x * 2) (1)) // 256
);
CPS一开始有点吓人。然而,它总是遵循相同的模式:最后一个参数是continuation(一个函数),它调用自己的主体:k => k(…)。请注意CPS将应用程序翻过来,即take(8)(重复…)变成k(take(8))(…)其中k是部分应用的重复。
结论
通过将重复(repeat)与终止条件(take)分离,我们获得了灵活性——将关注点分离到痛苦的尽头:D
我认为最好的解决方案是使用let:
for (let i=0; i<100; i++) …
这将为每个主体求值创建一个新的(可变的)i变量,并确保i只在循环语法中的增量表达式中更改,而不是从其他任何地方更改。
我可以作弊,自己做一个发电机。至少i++是看不到的:)
在我看来,这应该足够了。即使在纯语言中,所有的操作(或者至少它们的解释器)都是由使用突变的原语构建的。只要它的作用域是正确的,我看不出这有什么错。
你应该可以接受
function* times(n) {
for (let i = 0; i < n; 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);
但对我来说,这似乎有点过头了,可能会有性能问题(因为尾调用消除无法用于返回收益*)。
我用一个helper函数包装了@Tieme的答案。
在打字稿:
export const mapN = <T = any[]>(count: number, fn: (...args: any[]) => T): T[] => [...Array(count)].map((_, i) => fn())
现在你可以运行:
const arr: string[] = mapN(3, () => 'something')
// returns ['something', 'something', 'something']