Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

yield from a list of generators created from an array

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?

like image 591
georg Avatar asked Aug 16 '17 08:08

georg


People also ask

What are generators How does yielding work?

It returns only a single value to the caller, and the code execution stops as soon as it reaches the return statement. When a caller calls the generator function, the first yield is executed, and the function stops. It then returns the generator object to the caller where the value is stored.

How do you find the yield value?

The yield keyword converts the expression given into a generator function that gives back a generator object. To get the values of the object, it has to be iterated to read the values given to the yield.

Can a generator return a function value?

A return statement in a generator, when executed, will make the generator finish (i.e. the done property of the object returned by it will be set to true ). If a value is returned, it will be set as the value property of the object returned by the generator.

What is yield in react native?

yield can be used inside a generator function & it helps to pause and resume a function at any time asynchronously. Also Additionally it helps to return value from a generator function. Check this document for more information. call is redux-saga effects which help to make asynchronous calls.


2 Answers

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);
}
like image 146
Bergi Avatar answered Sep 16 '22 11:09

Bergi


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; }
like image 25
Nina Scholz Avatar answered Sep 20 '22 11:09

Nina Scholz