atom feed9 messages in net.php.lists.internals[PHP-DEV] List comprehensions and gen...
FromSent OnAttachments
Nikita PopovJun 28, 2012 3:48 am 
Jordi BoggianoJun 28, 2012 4:48 am 
Sebastian KrebsJun 28, 2012 5:43 am 
David MuirJun 28, 2012 6:22 am 
Jared WilliamsJun 28, 2012 6:23 am 
Sebastian KrebsJun 28, 2012 6:45 am 
Nikita PopovJun 28, 2012 10:05 am 
Nikita PopovJun 28, 2012 10:28 am 
David MuirJun 28, 2012 9:46 pm 
Subject:[PHP-DEV] List comprehensions and generator expressions for PHP
From:Nikita Popov (niki@gmail.com)
Date:Jun 28, 2012 3:48:56 am
List:net.php.lists.internals

Hi internals!

Python and several other languages include support for list comprehensions and generator expressions and I'd like to see something similar in PHP too.

I created a hacky proof of concept implementation here: https://github.com/nikic/php-src/tree/addListComprehensions. It's really dirty, but it implements both features in about ~150 lines of code.

Currently I'm using the following syntax:

$firstNames = [foreach ($users as $user) yield $user->firstName];

This code is roughly equivalent to writing:

$firstNames = []; foreach ($users as $user) { $firstNames[] = $user->firstName; }

You may notice that this particular list comprehension provides the same functionality as array_column(), just in a little more generalized way. E.g. you could use all of the following without having special functions for them all:

$firstNames = [foreach ($users as $user) yield $user->firstName];

$firstNames = [foreach ($users as $user) yield $user->getFirstName()];

$firstNames = [foreach ($users as $user) yield $user['firstName']];

It's also possible to explicitly specify a key:

$firstNames = [foreach ($users as $user) yield $user->id => $user->firstName];

It is also possible to filter elements using list comprehensions:

$underageUsers = [foreach ($users as $user) if ($user->age < 18) yield $user]; // or just the names $underageUserNames = [foreach ($users as $user) if ($user->age < 18) yield $user->firstName];

It is also possible to nest multiple foreach loops:

$aList = ['A', 'B']; $bList = [1, 2]; $combinations = [foreach ($aList as $a) foreach ($bList as $b) yield [$a, $b]]; // gives: [ ['A', 1], ['A', 2], ['B', 1], ['B', 2] ]

All the above are list comprehensions (or in PHP rather array comprehensions), i.e. they create an array as the result.

If this is not needed it is also possible to compute the values lazily using generator expressions, which use () instead of [].

$firstNames = (foreach ($users as $user) yield $user->firstName);

In this case $firstNames will no longer be an array of first names, but instead will be a generator producing first names.

This is handy if you only need to iterate the resulting "list" only once as it saves you holding the whole list in memory.

Also it allows you to work with infinite lists easily:

function *naturalNumbers() { for ($i = 0; ; ++$i) { yield $i; } }

// all natural numbers $numbers = naturalNumbers(); // only the odd ones $oddNumbers = (foreach ($numbers as $n) if ($n % 2) yield $n); // ...

(At this point I wonder whether one should include support for for-loops in list comprehensions. So the naturalNumbers() function could be replaced with (for ($i = 0;; ++$i) yield $i), etc)

So, what do you think? Do we want something like this in PHP?

Nikita