at.*_*at. 132 javascript generator ecmascript-harmony ecmascript-6
x在JavaScript中循环时间的典型方法是:
for (var i = 0; i < x; i++)
doStuff(i);
Run Code Online (Sandbox Code Playgroud)
但我不想使用++运算符或根本没有任何可变变量.那么,在ES6中,有x另一种方式循环时间吗?我喜欢Ruby的机制:
x.times do |i|
do_stuff(i)
end
Run Code Online (Sandbox Code Playgroud)
在JavaScript/ES6中有类似的东西吗?我可以作弊并制造自己的发电机:
function* times(x) {
for (var i = 0; i < x; i++)
yield i;
}
for (var i of times(5)) {
console.log(i);
}
Run Code Online (Sandbox Code Playgroud)
我当然还在用i++.至少它看不见:),但我希望在ES6中有更好的机制.
Tie*_*eme 206
[...Array(n)].map()const res = [...Array(10)].map((_, i) => {
return i * 10;
});
// as a one liner
const res = [...Array(10)].map((_, i) => i * 10);
Run Code Online (Sandbox Code Playgroud)
或者如果您不需要结果:
[...Array(10)].forEach((_, i) => {
console.log(i);
});
// as a one liner
[...Array(10)].forEach((_, i) => console.log(i));
Run Code Online (Sandbox Code Playgroud)
请注意,如果您只需要重复一个字符串,则可以使用 String.prototype.repeat.
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);
Run Code Online (Sandbox Code Playgroud)
Tha*_*you 137
好!
下面的代码是使用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'))Run Code Online (Sandbox Code Playgroud)
如果确实需要迭代器,可以使用带有计数器参数的命名内部函数来迭代
const times = n => f => {
let iter = i => {
if (i === n) return
f (i)
iter (i + 1)
}
return iter (0)
}
times (3) (i => console.log(i, 'hi'))Run Code Online (Sandbox Code Playgroud)
如果你不喜欢学习更多的东西,请不要在这里阅读...
但是对于那些......
if语句很难看 - 另一个分支上发生了什么?undefined- 表示不纯的,副作用的功能"难道没有更好的方法吗?"
有.让我们首先重新审视我们的初始实施
// times :: Int -> (void -> void) -> void
const times = x => f => {
if (x > 0) {
f() // has to be side-effecting function
times (x - 1) (f)
}
}Run Code Online (Sandbox Code Playgroud)
当然,这很简单,但请注意我们如何打电话f(),不做任何事情.这确实限制了我们可以重复多次的功能类型.即使我们有可用的迭代器,f(i)也不会更加通用.
如果我们从更好的函数重复过程开始怎么办?也许更好地利用输入和输出的东西.
通用函数重复
// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
if (n > 0)
return repeat (n - 1) (f) (f (x))
else
return x
}
// power :: Int -> Int -> Int
const power = base => exp => {
// repeat <exp> times, <base> * <x>, starting with 1
return repeat (exp) (x => base * x) (1)
}
console.log(power (2) (8))
// => 256Run Code Online (Sandbox Code Playgroud)
上面,我们定义了一个泛型repeat函数,它接受一个额外的输入,用于启动单个函数的重复应用.
// repeat 3 times, the function f, starting with x ...
var result = repeat (3) (f) (x)
// is the same as ...
var result = f(f(f(x)))
Run Code Online (Sandbox Code Playgroud)
实现times与repeat
那么现在这很容易; 几乎所有的工作都已完成.
// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
if (n > 0)
return repeat (n - 1) (f) (f (x))
else
return x
}
// times :: Int -> (Int -> Int) -> Int
const times = n=> f=>
repeat (n) (i => (f(i), i + 1)) (0)
// use it
times (3) (i => console.log(i, 'hi'))Run Code Online (Sandbox Code Playgroud)
由于我们的函数i作为输入并返回i + 1,因此这有效地作为我们f每次传递的迭代器.
我们也修复了问题的清单
if陈述undefinedJavaScript逗号运算符,
如果你无法看到最后一个例子是如何工作的,那么这取决于你对JavaScript最古老的战斧之一的认识; 的逗号操作符 -总之,它从左至右计算表达式和返回最后计算的表达式的值
(expr1 :: a, expr2 :: b, expr3 :: c) :: c
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,我正在使用
(i => (f(i), i + 1))
Run Code Online (Sandbox Code Playgroud)
这只是一种简洁的写作方式
(i => { f(i); return i + 1 })
Run Code Online (Sandbox Code Playgroud)
尾调用优化
由于递归实现的性感,在这一点上我推荐它们是不负责任的,因为没有我能想到的JavaScript VM支持正确的尾部调用消除 - babel用于转换它,但它已被"破坏;将重新实现"状态超过一年.
repeat (1e6) (someFunc) (x)
// => RangeError: Maximum call stack size exceeded
Run Code Online (Sandbox Code Playgroud)
因此,我们应该重新审视我们的实现,repeat以使其安全堆栈.
下面的代码确实使用了可变变量n,x但请注意所有突变都定位到repeat函数中 - 从函数外部看不到状态变化(突变)
// repeat :: Int -> (a -> a) -> (a -> a)
const repeat = n => f => x =>
{
let m = 0, acc = x
while (m < n)
(m = m + 1, acc = f (acc))
return acc
}
// inc :: Int -> Int
const inc = x =>
x + 1
console.log (repeat (1e8) (inc) (0))
// 100000000Run Code Online (Sandbox Code Playgroud)
这将有很多人说"但这不起作用!" - 我知道,放松吧.我们可以使用纯表达式实现Clojure样式loop/ recur接口以进行常量空间循环; 没有那些东西.while
在这里,我们while使用我们的loop函数抽象出来- 它寻找一种特殊的recur类型来保持循环运行.recur遇到非类型时,循环结束并返回计算结果
const recur = (...args) =>
({ type: recur, args })
const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}
const repeat = $n => f => x =>
loop ((n = $n, acc = x) =>
n === 0
? acc
: recur (n - 1, f (acc)))
const inc = x =>
x + 1
const fibonacci = $n =>
loop ((n = $n, a = 0, b = 1) =>
n === 0
? a
: recur (n - 1, b, a + b))
console.log (repeat (1e7) (inc) (0)) // 10000000
console.log (fibonacci (100)) // 354224848179262000000Run Code Online (Sandbox Code Playgroud)
zer*_*kms 31
for (let i of Array(100).keys()) {
console.log(i)
}
Run Code Online (Sandbox Code Playgroud)
Ber*_*rgi 24
我认为最好的解决方案是使用let:
for (let i=0; i<100; i++) …
Run Code Online (Sandbox Code Playgroud)
这将为i每个主体评估创建一个新的(可变)变量,并确保i仅在该循环语法中的增量表达式中更改,而不是从其他任何位置更改.
我可以欺骗自己的发电机.至少
i++是看不见:)
这应该足够了.即使在纯语言中,所有操作(或至少是它们的解释器)都是使用使用变异的原语构建的.只要它适当的范围,我看不出有什么问题.
你应该没问题
function* times(n) {
for (let i = 0; i < x; i++)
yield i;
}
for (const i of times(5))
console.log(i);
Run Code Online (Sandbox Code Playgroud)
但我不想使用
++运算符或根本没有任何可变变量.
那么你唯一的选择是使用递归.您也可以在没有可变的情况下定义该生成器函数i:
function* range(i, n) {
if (i >= n) return;
yield i;
return yield* range(i+1, n);
}
times = (n) => range(0, n);
Run Code Online (Sandbox Code Playgroud)
但这对我来说似乎有些过分,并且可能存在性能问题(因为尾部调用消除不可用return yield*).
oem*_*era 24
这是另一个不错的选择:
Array.from({ length: 3}).map(...);
Run Code Online (Sandbox Code Playgroud)
最好,正如@Dave Morse 在评论中指出的那样,您还可以map通过使用Array.from函数的第二个参数来摆脱调用,如下所示:
Array.from({ length: 3 }, () => (...))
Run Code Online (Sandbox Code Playgroud)
Hos*_*rad 12
const times = 4;
new Array(times).fill().map(() => console.log('test'));
Run Code Online (Sandbox Code Playgroud)
这个片段将是console.log test4次.
arc*_*don 11
答案:2015年12月9日
就个人而言,我发现接受的答案既简洁(好)又简洁(坏).欣赏这个陈述可能是主观的,所以请阅读这个答案,看看你是否同意或不同意
问题中给出的例子类似于Ruby:
x.times do |i|
do_stuff(i)
end
Run Code Online (Sandbox Code Playgroud)
使用下面的JS表达这一点将允许:
times(x)(doStuff(i));
Run Code Online (Sandbox Code Playgroud)
这是代码:
let times = (n) => {
return (f) => {
Array(n).fill().map((_, i) => f(i));
};
};
Run Code Online (Sandbox Code Playgroud)
而已!
简单示例用法:
let cheer = () => console.log('Hip hip hooray!');
times(3)(cheer);
//Hip hip hooray!
//Hip hip hooray!
//Hip hip hooray!
Run Code Online (Sandbox Code Playgroud)
或者,按照接受的答案的例子:
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'
Run Code Online (Sandbox Code Playgroud)
附注 - 定义范围功能
一个类似/相关的问题,使用基本上非常相似的代码结构,可能是(核心)JavaScript中有一个方便的Range函数,类似于下划线的范围函数.
从x开始创建一个包含n个数字的数组
下划线
_.range(x, x + n)
Run Code Online (Sandbox Code Playgroud)
ES2015
几种替代品:
Array(n).fill().map((_, i) => x + i)
Array.from(Array(n), (_, i) => x + i)
Run Code Online (Sandbox Code Playgroud)
使用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 ]
Run Code Online (Sandbox Code Playgroud)
在一个快速测试中,我使用我们的解决方案和doStuff函数运行上述每个运行一百万次,前一种方法(Array(n).fill())证明稍微快一些.
Ger*_*ári 10
我认为这很简单:
[...Array(3).keys()]
Run Code Online (Sandbox Code Playgroud)
要么
Array(3).fill()
Run Code Online (Sandbox Code Playgroud)
J G*_*cia 10
我迟到了,但由于这个问题经常出现在搜索结果中,我只想添加一个我认为在可读性方面最好的解决方案,但不长(这对于任何代码库 IMO 都是理想的) . 它会变异,但我会为 KISS 原则做出权衡。
let times = 5
while( times-- )
console.log(times)
// logs 4, 3, 2, 1, 0
Run Code Online (Sandbox Code Playgroud)
Array(100).fill().map((_,i)=> console.log(i) );
Run Code Online (Sandbox Code Playgroud)
该版本满足OP对不变性的要求.也可以考虑使用reduce而不是map 根据您的使用情况.
如果您不介意原型中的一点突变,这也是一个选项.
Number.prototype.times = function(f) {
return Array(this.valueOf()).fill().map((_,i)=>f(i));
};
Run Code Online (Sandbox Code Playgroud)
现在我们可以做到这一点
((3).times(i=>console.log(i)));
Run Code Online (Sandbox Code Playgroud)
.fill建议+1到arcseldon .
不是我会教的东西(或者在我的代码中使用过),但是这里有一个代码高尔夫的解决方案而不需要改变变量,不需要ES6:
Array.apply(null, {length: 10}).forEach(function(_, i){
doStuff(i);
})
Run Code Online (Sandbox Code Playgroud)
真的,更多的是一个有趣的概念验证,而不是一个有用的答案.
| 归档时间: |
|
| 查看次数: |
72638 次 |
| 最近记录: |