Skip to content

Instantly share code, notes, and snippets.

@dhrrgn
Last active December 20, 2015 12:39
Show Gist options
  • Save dhrrgn/6132974 to your computer and use it in GitHub Desktop.
Save dhrrgn/6132974 to your computer and use it in GitHub Desktop.

In PHP we have the for and foreach loops, which are both handy, but there is a case where a 3rd type of for loop would be handy: Iterators.

With 5.5 it became super easy to use Iterators, by simple creating a Generator. While working on my itertools port to PHP (here), I found a situation where both the for and foreach constructs fell short.

Example

I had created counter Generator which just creates an Iterator that starts at a given number and counts indefinitely. Then, I wanted to create a version of range which used an Iterator. Now, I could do this with a few for loops like in the PHP docs, but I wanted to use my new fancy counter function, so I started with a for loop:

<?php
for ($c = counter($start, $step); $c->current() <= $end; $c->next()) {
    yield $c->current();
}

Pretty simple, but ugly. So I tried it with a foreach loop:

<?php
foreach (counter($start, $step) as $i) {
    yield $i;
    if ($i === $end) {
        break;
    }
}

A little bit better, but could still be better.

The foreach until Loop

After being annoyed by this, I had an idea for a new type of loop; the "foreach until" loop:

<?php
foreach (counter($start, $step) as $i) until ($i === $end) {
    yield $i;
}

This could be useful in all sorts of situations. I know a lot of people may not want to add a new keyword to PHP, so there is an alternative as well.

The foreach while Loop

Same thing as above, except it is "while n is true" instead of "until n is true":

<?php
foreach (counter($start, $step) as $i) while ($i !== $end) {
    yield $i;
}

Thoughts?

Order of Execution

The while or until checks should happen after the body of the foreach has been executed. This will keep it in-line with the current do...while loop construct.

Alternative Syntax

We could also keep it more in-line with the do...while construct and append the while or until expressions to the end of the loop:

<?php
foreach (counter($start, $step) as $i) {
    yield $i;
} until ($i === $end);

and

<?php
foreach (counter($start, $step) as $i) {
    yield $i;
} while ($i !== $end);

I do not prefer this syntax personally, but it is more consistent with the other loops.

@dhrrgn
Copy link
Author

dhrrgn commented Aug 1, 2013

Another Example

You could be looping over all the files in a directory and parsing them. If you wanted to allow something to interrupt it, you could add an until (check_for_interrupt()) or while ( ! check_for_interrupt()).

@craighooghiem
Copy link

I definitely like this idea.
Especially the until (check_for_interrupt()) example.

Makes perfect sense.

@nikic
Copy link

nikic commented Aug 1, 2013

At least you particular example here can be elegantly solved using a function like takeWhile:

return takeWhile(fn\op('!==', $end), counter($start, $step));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment