iter
iter copied to clipboard
Feature request for iter\head
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?
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.
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.
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.
Oops I meant but there's no immutability.
I encountered the above trying to implement a lazy unzip. For reference, see: https://gist.github.com/CMCDragonkai/2ad2359a961ff13c82327da2fea0b9d8
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;
}
}
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