Ruby-like iterators in PHP

I’ve started playing with Ruby recently, and one of the things that got my attention in Ruby were iterators. They are different inside from regular loops but work in a similar way, and looks like people (at least ones that write tutorials and code examples ;) ) like to use them. For example, you can have:

arr = {"one" => 1, "two" => 2, "three" => 3}
arr.each do |key, val|
print "#{key}: is #{val}\n"
end

which iterates over a Ruby hash and prints:

three: is 3
two: is 2
one: is 1

So it got me thinking – suppose I wanted to do something like this in PHP (suppose I don’t like regular loop-y iterators for some weird reason). Naturally, I wouldn’t get it in the same concise form as Ruby does, since I can’t change the syntax. But I could get the essence. Let’s try it. First, the main iterator:

class RubyIterator {
  protected $_body;

  public function __construct($body) {
    if(!is_callable($body)) {
      throw new Exception("Iterator body should be a callable");
    }
    $this->_body = $body;
  }
  public function yield()
  {
    $args = func_get_args();
    call_user_func_array($this->_body, $args);
  }
}

Next, less try to make some class that uses it:

class RubyArray {
    protected $_arr;
    public function __construct(array $a)
    {
        $this->_arr $a;
    }

    public function each($body) {
        $iter = new RubyIterator($body);
        foreach($this->_arr as $k => $v) {
            $iter->yield($k$v);
        }
    }
}

and then:

/*
arr = {"one" => 1, "two" => 2, "three" => 3}
arr.each do |key, val|
    print "#{key}: is #{val}\n"
end
*/
    
$arr = new RubyArray(array("one" => 1"two" => 2"three" => 3));
$arr->each(function($key$val) { echo "$key: is $val\n"; });

and the result the same, of course. Or, let’s try with ranges:

class RubyRange {
    protected $_from$_to;
    public function __construct($from$to) {
        $this->_from $from;
        $this->_to $to;
    }
    public function each($body) {
        $iter = new RubyIterator($body);
        for($i=$this->_from$i<=$this->_to$i++) {
            $iter->yield($i);
        }
    }
    
}

and use it:

/*
r = 1..10;
r.each do |i|
    print "#{i*i}\n"
end
*/

$rr = new RubyRange(110);
$rr->each(function($i) { echo $i*$i."\n"; });

which indeed produces:

1
4
9
16
25
36
49
64
81
100

And so on – if one wanted, whole set of iterator methods could be implemented (I’m of course too lazy to do that :) ). I wonder if there are use cases that we can’t do there.

About these ads

26 thoughts on “Ruby-like iterators in PHP

  1. Pingback: [Web] 連結分享 | Geek is a Lift-Style.

  2. First of all, good attempt and new trial on PHP.

    But to add more this,
    1. PHP and ruby are different and accepting the difference is what makes us code better in the specific language.
    2. We can inherit / welcome new features of other language, if it is really easy to use(like RoR based frameworks got into PHP), while the above makes an overload to make simple things available in PHP.

    Again, It is good and appreciative to try something new.. but why not try, that is really innovative and useful?

  3. As a professional PHP developer I am not sure where I would use this feature. I understand why you have pursued the idea and its nice to see thing like this from time to time. It could be a bit confusing though for newbies and as others have said its not really necessary. Although thats not the point is it I guess. Wait even I’m confusing myself now….lol

  4. Pingback: Max’ Lesestoff zum Wochenende | PHP hates me - Der PHP Blog

  5. I don’t get what’s the point of RubyIterator? It doesn’t seem to have anything to do with iterators. It just wraps calling a function object as a single method interface. Seems kind of pointless with first class functions that you could call directly.

    What would be actually quite useful would be an SPL iterator implementation of Python’s itertools module providing lazy iterators for map, filter, slice, zip, chain and other operations. Some of them are actually there in SPL, but having to subclass the Iterator classes makes them hopelessly verbose and puts the relevant code out of band. Implementing them without the yield keyword is rather tedious. Whether they are methods on a collection class or just functions/static methods is only a syntactic difference – whether the actions to perform read outside in or inside out.

    The syntax would be even nicer if PHP supported destructuring assignment list($x,$y) in foreach.

  6. Pingback: Stanislav Malyshev’s Blog: Ruby-like iterators in PHP | Webs Developer

  7. i still don’t get it … what’s the difference between your iterator class and for example:

    array_map(function(…) {
    }, …);

    ? what can do the ruby-iterator class better than array_map or the SPL iterators?

    • The difference is it doesn’t have to be an array and you can do other things beside iterating over elements in a linear fashion (see what they do in Ruby with iterators, simplest example would be .times, but there are much more complex ones – like walking a tree). It’s decoupling of data generation from the action.
      As for better – it’s in the eyes of beholder. I don’t think it makes sense to claim one way is “better” than other without saying better for whom and for what purpose.

    • Bungle, the magic is in the yield method… in ruby at leas

      But this isn’t yielding… it’s just calling the passed callback. Nothing to do with yielding, even if you have method named yield. PHP doesn’t support yield, and it cannot be hacked in with classes. Only way to do it is to change the language to support it.

  8. What is there in Ruby iterators, that cannot be implemented using PHP foreach loop?

    arr.each do |key, val|
    print “#{key}: is #{val}\n”
    end

    foreach (arr as $key => $val) {
    echo “$key: is $val”;
    }

    • From the fucking article:

      So it got me thinking – suppose I wanted to do something like this in PHP (suppose I don’t like regular loop-y iterators for some weird reason). Naturally, I wouldn’t get it in the same concise form as Ruby does, since I can’t change the syntax. But I could get the essence. Let’s try it.

  9. Hi,

    I implemented all PHP types as well as some other additional types. It is nice to allow you to work with PHP data types as objects. Method calls are chainable which allows for some nice syntaxes:

    http://github.com/jwage/DurrProject

    $array = Type::arr()
    ->_explode(‘test1, test2, test3′)
    ->_reverse();

    $this->assertEquals(array(‘test3′, ‘test2′, ‘test1′), $array->getValue());

    • This is so much cleaner:
      $array = array_ reverse(explode(‘test1, test2, test3′));

      PHP has a great data structure called array. It also has huge number of inbuilt functionality to work with the arrays (http://www.php.net/manual/en/ref.array.php + iterators). Using objects to wrap this, is only obfuscating the whole thing. Introducing new unneeded needed datatypes (Type::arr()) etc. only binds your data (= array) with functionality. In C-like environments (that I count PHP too), it’s better to have less and pure data types (no data encapsulation) and more functions to work with them.

      • This blog is for thinking outside the box. Let go of your limited vision. The purpose of making this stuff is not with the intentions of people using it, it is just to help with expanding your thoughts. Think of new ways to do things. Don’t take it so personally and get so defensive :) Anyways, I wrote that code not for me to use, I wrote it because I was playing with PHP5.3 and needed some stupid pilot project to play with. Hence the name “Durr”…

        • Jonathan, you got it exactly right, thanks! We do some stuff just because we can, and it looks fun to. It doesn’t have to be all narrowly practical. If it will turn out practical – it’s a nice bonus :)

          • First of all I didn’t took anything personally. I’m all into learning new ways and techniques. I only shared my point of view in the comments. That is the main reason of having comments in a blog – to get conversation of used technique.

Comments are closed.