Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request for iter\head #38

Open
CMCDragonkai opened this issue Apr 7, 2017 · 7 comments
Open

Feature request for iter\head #38

CMCDragonkai opened this issue Apr 7, 2017 · 7 comments

Comments

@CMCDragonkai
Copy link

The taken and slice variants don't consume the generator. I need the equivalent of array_splice or head in haskell. Take the head off the iterable, and consume it, so that when I use the iterable next, it doesn't have the head anymore. How can this be done?

@CMCDragonkai
Copy link
Author

CMCDragonkai commented Apr 7, 2017

One way is to do:

return [iter\take(1, $arr), iter\drop(1, $arr)];

But I'm not sure if that has side-effects for non-rewindable generators. It fails because the subsequent use of the generator complains about already ran generator.

It works for static arrays, but generators it fails, having a head that eagerly gives back the first result and returns the tail generator is very useful.

@nikic
Copy link
Owner

nikic commented Apr 12, 2017

I'm afraid this is not really possible in the case of generators. The issue is that foreach performs an implicit rewind and generators only allow rewinds on generators where no elements have been consumed yet. See for example: https://3v4l.org/X6ssY

The only way to split a generator in head + tail (where the tail is still usable as a normal iterator in foreach) would be to create an entirely new generator that passes through all values, so something like this: https://3v4l.org/34hPC

Due to how yield from is implemented I think this will not perform quite as horribly as one might think, but it's still not a good solution.

@CMCDragonkai
Copy link
Author

I tried the generator functions to acquire the current head, but there's immutability when passing the generator instance to 2 or more functions, so heading off the generator in one function results in a mutation of the second generator. One way to solve this is structure sharing.

@CMCDragonkai
Copy link
Author

Oops I meant but there's no immutability.

@CMCDragonkai
Copy link
Author

I encountered the above trying to implement a lazy unzip. For reference, see: https://gist.github.com/CMCDragonkai/2ad2359a961ff13c82327da2fea0b9d8

@ktomk
Copy link

ktomk commented Jul 6, 2017

To consume from an iterable, it needs to be an Iterator which does not rewind. Decorating an iterable with a NoRewindIterator can do this:

/**
 * forward only iteration for an iterable
 */
final class NoRewindIterableIterator extends NoRewindIterator
{
    public function __construct(iterable $iterable)
    {
        parent::__construct($this->yieldFrom($iterable));
    }

    private function yieldFrom(iterable $iterable): Generator
    {
        yield from $iterable;
    }
}

@drupol
Copy link

drupol commented Sep 26, 2020

I encountered the above trying to implement a lazy unzip. For reference, see: https://gist.github.com/CMCDragonkai/2ad2359a961ff13c82327da2fea0b9d8

I implemented a lazy Zip/Unzip as well:

$c = Collection::fromIterable([1, 2])
    ->zip([3, 4]);

print_r($c->all()); // [[1,3], [2, 4]]

Find it here: https://github.com/loophp/collection

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants