Выход из списка генераторов, созданных из массива

У меня есть этот рекурсивный генератор

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)])

Ответ 1

Вы можете использовать параметры отдыха ... и проверить длину оставшегося массива для другого вызова генератора

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; }

Ответ 2

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; }

Ответ 3

Есть ли способ записать его функционально, без 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);
}

Ответ 4

Вы можете уменьшить массив до генератора. Но это выглядит хуже, чем для цикла для меня (хотя функционально:))

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)])

Ответ 5

может быть что-то вроде

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)]);