У меня есть этот рекурсивный генератор
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)])
У меня есть этот рекурсивный генератор
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)])
Вы можете использовать параметры отдыха ...
и проверить длину оставшегося массива для другого вызова генератора
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; }
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; }
Есть ли способ записать его функционально, без
for
циклов?
Нет, не совсем. (Конечно, вы всегда можете выбрать рекурсию, но я буду подвергать сомнению полезность этого подхода).
Мы ищем функциональные комбинаторы для итераторов:
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)) :-)
Теперь мы можем просто рассматривать итераторы как монаду и больше не беспокоиться о реализации.
Как вы можете видеть, я не использовал синтаксис 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))
yield * x.reduceRight(
(f, y) => function*() {
yield * flat(y);
yield * f()
},
function*() {}
)()
else
yield 'foo' + x;
}
console.log([...flat(obj)])
может быть что-то вроде
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)]);