从从数组创建的生成器列表中产生
yield from a list of generators created from an array
我有这个递归生成器
var obj = [1,2,3,[4,5,[6,7,8],9],10]
function *flat(x) {
if (Array.isArray(x))
for (let y of x)
yield *flat(y)
else
yield 'foo' + x;
}
console.log([...flat(obj)])
它工作正常,但我不喜欢 for
部分。有没有办法在功能上编写它?我试过了
if (Array.isArray(x))
yield *x.map(flat)
没用。
有没有一种方法可以不用 for
循环来编写上述函数?
您可以将数组缩减为生成器。但这对我来说看起来比 for 循环更糟糕(虽然是功能性的:))
var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10]
function* flat(x) {
if (Array.isArray(x))
yield * x.reduceRight(
(f, y) => function*() {
yield * flat(y);
yield * f()
},
function*() {}
)()
else
yield 'foo' + x;
}
console.log([...flat(obj)])
您可以使用 rest parameters ...
并检查剩余数组的长度,以便再次调用生成器
function* flat(a, ...r) {
if (Array.isArray(a)) {
yield* flat(...a);
} else {
yield 'foo' + a;
}
if (r.length) {
yield* flat(...r);
}
}
var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10];
console.log([...flat(obj)])
.as-console-wrapper { max-height: 100% !important; top: 0; }
一种类似的方法,但使用 spread
生成器来调用具有展开值的移交生成器。
function* spread(g, a, ...r) {
yield* g(a);
if (r.length) {
yield* spread(g, ...r);
}
}
function* flat(a) {
if (Array.isArray(a)) {
yield* spread(flat, ...a);
} else {
yield 'foo' + a;
}
}
var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10];
console.log([...flat(obj)])
.as-console-wrapper { max-height: 100% !important; top: 0; }
可能类似于
var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10];
function* flat(x) {
if (Array.isArray(x)) {
yield x.map(v => {
return [...flat(v)].join();
});
} else yield "foo" + x;
}
console.log([...flat(obj)]);
map
是个好主意,但您需要将生成的生成器对象数组减少为一个生成器对象:
function *flat(x) {
if (Array.isArray(x))
yield *x.map(flat).reduce((a, b) => function*() { yield *a; yield *b }());
else
yield 'foo' + x;
}
var obj = [1,2,3,[4,5,[6,7,8],9],10];
console.log([...flat(obj)]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Is there a way to write it functionally, without for
loops?
不,不是真的。 (当然你总是可以选择递归,但我会质疑这种方法的用处)。
我们正在寻找的是迭代器的功能组合器:
function* of(x) { // also known as `pure` or `return`
yield x;
}
function map(f) { return function* (xs) { // also known as `fmap`
for (const x of xs)
yield f(x);
}
function* join(xss) { // also known as `concat` (not `append`!) or `flatten` (but non-recursive!)
for (const xs of xss)
for (const x of xs)
yield x;
}
function chain(f) { return function* (xs) { // also known as `concatMap` or `bind`
for (const x of xs)
const ys = f(x);
for (const y of ys)
yield y;
}
// or const chain = f => compose(concat, map(f)) :-)
现在我们可以将迭代器视为一个 monad,而不用再关心实现。
如您所见,我没有使用上面的语法 yield* xs
,它(基本上)只是糖分
for (const x of xs)
yield x;
在您的实现中看起来很奇怪的是外部循环和内部非循环之间的差异。在一个最佳世界中,会有一个 yield**
语法 做 join
做的事情,但没有。所以我们只能用上面的辅助函数来很好的实现你的功能:
function* flat(x) {
if (Array.isArray(x))
yield* chain(flat)(x);
else
yield* of('foo' + x); // foreshadowing
}
或者只是
function flat(x) {
return Array.isArray(x) ? chain(flat)(x) : of('foo' + x);
}
我有这个递归生成器
var obj = [1,2,3,[4,5,[6,7,8],9],10]
function *flat(x) {
if (Array.isArray(x))
for (let y of x)
yield *flat(y)
else
yield 'foo' + x;
}
console.log([...flat(obj)])
它工作正常,但我不喜欢 for
部分。有没有办法在功能上编写它?我试过了
if (Array.isArray(x))
yield *x.map(flat)
没用。
有没有一种方法可以不用 for
循环来编写上述函数?
您可以将数组缩减为生成器。但这对我来说看起来比 for 循环更糟糕(虽然是功能性的:))
var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10]
function* flat(x) {
if (Array.isArray(x))
yield * x.reduceRight(
(f, y) => function*() {
yield * flat(y);
yield * f()
},
function*() {}
)()
else
yield 'foo' + x;
}
console.log([...flat(obj)])
您可以使用 rest parameters ...
并检查剩余数组的长度,以便再次调用生成器
function* flat(a, ...r) {
if (Array.isArray(a)) {
yield* flat(...a);
} else {
yield 'foo' + a;
}
if (r.length) {
yield* flat(...r);
}
}
var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10];
console.log([...flat(obj)])
.as-console-wrapper { max-height: 100% !important; top: 0; }
一种类似的方法,但使用 spread
生成器来调用具有展开值的移交生成器。
function* spread(g, a, ...r) {
yield* g(a);
if (r.length) {
yield* spread(g, ...r);
}
}
function* flat(a) {
if (Array.isArray(a)) {
yield* spread(flat, ...a);
} else {
yield 'foo' + a;
}
}
var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10];
console.log([...flat(obj)])
.as-console-wrapper { max-height: 100% !important; top: 0; }
可能类似于
var obj = [1, 2, 3, [4, 5, [6, 7, 8], 9], 10];
function* flat(x) {
if (Array.isArray(x)) {
yield x.map(v => {
return [...flat(v)].join();
});
} else yield "foo" + x;
}
console.log([...flat(obj)]);
map
是个好主意,但您需要将生成的生成器对象数组减少为一个生成器对象:
function *flat(x) {
if (Array.isArray(x))
yield *x.map(flat).reduce((a, b) => function*() { yield *a; yield *b }());
else
yield 'foo' + x;
}
var obj = [1,2,3,[4,5,[6,7,8],9],10];
console.log([...flat(obj)]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Is there a way to write it functionally, without
for
loops?
不,不是真的。 (当然你总是可以选择递归,但我会质疑这种方法的用处)。
我们正在寻找的是迭代器的功能组合器:
function* of(x) { // also known as `pure` or `return`
yield x;
}
function map(f) { return function* (xs) { // also known as `fmap`
for (const x of xs)
yield f(x);
}
function* join(xss) { // also known as `concat` (not `append`!) or `flatten` (but non-recursive!)
for (const xs of xss)
for (const x of xs)
yield x;
}
function chain(f) { return function* (xs) { // also known as `concatMap` or `bind`
for (const x of xs)
const ys = f(x);
for (const y of ys)
yield y;
}
// or const chain = f => compose(concat, map(f)) :-)
现在我们可以将迭代器视为一个 monad,而不用再关心实现。
如您所见,我没有使用上面的语法 yield* xs
,它(基本上)只是糖分
for (const x of xs)
yield x;
在您的实现中看起来很奇怪的是外部循环和内部非循环之间的差异。在一个最佳世界中,会有一个 yield**
语法 做 join
做的事情,但没有。所以我们只能用上面的辅助函数来很好的实现你的功能:
function* flat(x) {
if (Array.isArray(x))
yield* chain(flat)(x);
else
yield* of('foo' + x); // foreshadowing
}
或者只是
function flat(x) {
return Array.isArray(x) ? chain(flat)(x) : of('foo' + x);
}