如何获取 JavaScript 生成器的第 n 个值?
How to get the nth value of a JavaScript generator?
如何获取生成器的第 n 个值?
function *index() {
let x = 0;
while(true)
yield x++;
}
// the 1st value
let a = index();
console.log(a.next().value); // 0
// the 3rd value
let b = index();
b.next();
b.next();
console.log(b.next().value); // 2
// the nth value?
let c = index();
let n = 10;
console.log(...); // 9
一个简单的循环即可:
let n = 10,
iter = index();
while (--n > 0) iter.next();
console.log(iter.next().value); // 9
,无法直接获取第 n
个元素,因为值是按需生成的,并且只能使用 next
检索即时值功能。因此,我们需要明确跟踪消耗的物品数量。
唯一的解决办法是使用循环,我更喜欢用 for..of
迭代它。
我们可以创建这样的函数
function elementAt(generator, n) {
"use strict";
let i = 0;
if (n < 0) {
throw new Error("Invalid index");
}
for (let value of generator) {
if (i++ == n) {
return value;
}
}
throw new Error("Generator has fewer than " + n + " elements");
}
然后像这样调用它
console.log(elementAt(index(), 10));
// 10
另一个有用的函数可能是 take
,它允许您从生成器中获取前 n
个元素,就像这样
function take(generator, n) {
"use strict";
let i = 1,
result = [];
if (n <= 0) {
throw new Error("Invalid index");
}
for (let value of generator) {
result.push(value);
if (i++ == n) {
return result;
}
}
throw new Error("Generator has fewer than " + n + " elements");
}
console.log(take(index(), 10))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
你可以定义一个枚举方法,如in python:
function *enumerate(it, start) {
start = start || 0;
for(let x of it)
yield [start++, x];
}
然后:
for(let [n, x] of enumerate(index()))
if(n == 6) {
console.log(x);
break;
}
http://www.es6fiddle.net/ia0rkxut/
同理,也可以重新实现 pythonic range
和 islice
:
function *range(start, stop, step) {
while(start < stop) {
yield start;
start += step;
}
}
function *islice(it, start, stop, step) {
let r = range(start || 0, stop || Number.MAX_SAFE_INTEGER, step || 1);
let i = r.next().value;
for(var [n, x] of enumerate(it)) {
if(n === i) {
yield x;
i = r.next().value;
}
}
}
然后:
console.log(islice(index(), 6, 7).next().value);
http://www.es6fiddle.net/ia0s6amd/
现实世界的实施需要更多的工作,但您明白了。
您可以创建一个大小为 n 的数组,并使用 Array.from
及其第二个参数来获取您需要的值。所以假设 iter
是来自生成器 gen
:
的迭代器
var iter = gen();
然后前 n 个值可以如下获取:
var values = Array.from(Array(n), iter.next, iter).map(o => o.value)
...当您只对 nth 值感兴趣时,您可以跳过 map
部分,然后做:
var value = Array.from(Array(n), iter.next, iter).pop().value
或:
var value = [...Array(n)].reduce(iter.next.bind(iter), 1).value
缺点是您仍然(暂时)分配大小为 n 的数组。
我想避免不必要地创建数组或其他中间值。这是我对 nth
的实现结果 -
function nth (iter, n)
{ for (const v of iter)
if (--n < 0)
return v
}
按照原问题中的例子-
// the 1st value
console.log(nth(index(), 0))
// the 3rd value
console.log(nth(index(), 2))
// the 10th value
console.log(nth(index(), 9))
0
2
9
对于有限生成器,如果索引超出范围,结果将为 undefined
-
function* foo ()
{ yield 1
yield 2
yield 3
}
console.log(nth(foo(), 99))
undefined
展开下面的代码片段以在浏览器中验证结果 -
function *index ()
{ let x = 0
while (true)
yield x++
}
function* foo ()
{ yield 1
yield 2
yield 3
}
function nth (iter, n) {
for (const v of iter)
if (--n < 0)
return v
}
// the 1st value
console.log(nth(index(), 0))
// the 3rd value
console.log(nth(index(), 2))
// the 10th value?
console.log(nth(index(), 9))
// out-of-bounds?
console.log(nth(foo(), 99))
如何获取生成器的第 n 个值?
function *index() {
let x = 0;
while(true)
yield x++;
}
// the 1st value
let a = index();
console.log(a.next().value); // 0
// the 3rd value
let b = index();
b.next();
b.next();
console.log(b.next().value); // 2
// the nth value?
let c = index();
let n = 10;
console.log(...); // 9
一个简单的循环即可:
let n = 10,
iter = index();
while (--n > 0) iter.next();
console.log(iter.next().value); // 9
n
个元素,因为值是按需生成的,并且只能使用 next
检索即时值功能。因此,我们需要明确跟踪消耗的物品数量。
唯一的解决办法是使用循环,我更喜欢用 for..of
迭代它。
我们可以创建这样的函数
function elementAt(generator, n) {
"use strict";
let i = 0;
if (n < 0) {
throw new Error("Invalid index");
}
for (let value of generator) {
if (i++ == n) {
return value;
}
}
throw new Error("Generator has fewer than " + n + " elements");
}
然后像这样调用它
console.log(elementAt(index(), 10));
// 10
另一个有用的函数可能是 take
,它允许您从生成器中获取前 n
个元素,就像这样
function take(generator, n) {
"use strict";
let i = 1,
result = [];
if (n <= 0) {
throw new Error("Invalid index");
}
for (let value of generator) {
result.push(value);
if (i++ == n) {
return result;
}
}
throw new Error("Generator has fewer than " + n + " elements");
}
console.log(take(index(), 10))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
你可以定义一个枚举方法,如in python:
function *enumerate(it, start) {
start = start || 0;
for(let x of it)
yield [start++, x];
}
然后:
for(let [n, x] of enumerate(index()))
if(n == 6) {
console.log(x);
break;
}
http://www.es6fiddle.net/ia0rkxut/
同理,也可以重新实现 pythonic range
和 islice
:
function *range(start, stop, step) {
while(start < stop) {
yield start;
start += step;
}
}
function *islice(it, start, stop, step) {
let r = range(start || 0, stop || Number.MAX_SAFE_INTEGER, step || 1);
let i = r.next().value;
for(var [n, x] of enumerate(it)) {
if(n === i) {
yield x;
i = r.next().value;
}
}
}
然后:
console.log(islice(index(), 6, 7).next().value);
http://www.es6fiddle.net/ia0s6amd/
现实世界的实施需要更多的工作,但您明白了。
您可以创建一个大小为 n 的数组,并使用 Array.from
及其第二个参数来获取您需要的值。所以假设 iter
是来自生成器 gen
:
var iter = gen();
然后前 n 个值可以如下获取:
var values = Array.from(Array(n), iter.next, iter).map(o => o.value)
...当您只对 nth 值感兴趣时,您可以跳过 map
部分,然后做:
var value = Array.from(Array(n), iter.next, iter).pop().value
或:
var value = [...Array(n)].reduce(iter.next.bind(iter), 1).value
缺点是您仍然(暂时)分配大小为 n 的数组。
我想避免不必要地创建数组或其他中间值。这是我对 nth
的实现结果 -
function nth (iter, n)
{ for (const v of iter)
if (--n < 0)
return v
}
按照原问题中的例子-
// the 1st value
console.log(nth(index(), 0))
// the 3rd value
console.log(nth(index(), 2))
// the 10th value
console.log(nth(index(), 9))
0
2
9
对于有限生成器,如果索引超出范围,结果将为 undefined
-
function* foo ()
{ yield 1
yield 2
yield 3
}
console.log(nth(foo(), 99))
undefined
展开下面的代码片段以在浏览器中验证结果 -
function *index ()
{ let x = 0
while (true)
yield x++
}
function* foo ()
{ yield 1
yield 2
yield 3
}
function nth (iter, n) {
for (const v of iter)
if (--n < 0)
return v
}
// the 1st value
console.log(nth(index(), 0))
// the 3rd value
console.log(nth(index(), 2))
// the 10th value?
console.log(nth(index(), 9))
// out-of-bounds?
console.log(nth(foo(), 99))