yield from a list of generators created from an array

时光怂恿深爱的人放手 提交于 2019-12-28 04:23:10

问题


I've got this recursive generator

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

It works fine, but I don't like the for part. Is there a way to write it functionally? I tried

if (Array.isArray(x))
   yield *x.map(flat)

which didn't work.

Is there a way to write the above function without for loops?


回答1:


You could use rest parameters ... and check the length of the rest array for another calling of the generator

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

A similar approach but with a spread generator for calling the handed over generator with the spreaded values.

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



回答2:


The map is a good idea, but you need to reduce the resulting array of generator objects to just one generator object:

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:


Is there a way to write it functionally, without for loops?

No, not really. (Of course you can always opt for recursion instead, but I'll question the usefulness of that approach).

What we're looking for are functional combinators for iterators:

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

Now we can just treat iterators as a monad, and be no more concerned about the implementation.

As you can see, I have not used the syntax yield* xs above which is (basically) just sugar for

for (const x of xs)
    yield x;

What is looking weird in your implementation is the disparity between the outer loop and the inner non-loop. In an optimal world, there would be a yield** syntax that did what join does, but there's not. So we can only implement your function nicely with the above helper functions:

function* flat(x) {
    if (Array.isArray(x))
        yield* chain(flat)(x);
    else
        yield* of('foo' + x); // foreshadowing
}

or just

function flat(x) {
    return Array.isArray(x) ? chain(flat)(x) : of('foo' + x);
}



回答4:


You could reduce array to generator. But this looks worse than for loop to me (though is functional :) )

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:


may be something like

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


来源:https://stackoverflow.com/questions/45708712/yield-from-a-list-of-generators-created-from-an-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!