Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:61025 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 50136 invoked from network); 28 Jun 2012 13:23:06 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 28 Jun 2012 13:23:06 -0000 Authentication-Results: pb1.pair.com smtp.mail=jared.williams1@ntlworld.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=jared.williams1@ntlworld.com; sender-id=pass Received-SPF: pass (pb1.pair.com: domain ntlworld.com designates 81.103.221.47 as permitted sender) X-PHP-List-Original-Sender: jared.williams1@ntlworld.com X-Host-Fingerprint: 81.103.221.47 mtaout01-winn.ispmail.ntl.com Solaris 10 (beta) Received: from [81.103.221.47] ([81.103.221.47:32545] helo=mtaout01-winn.ispmail.ntl.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id A5/9D-08168-8BA5CEF4 for ; Thu, 28 Jun 2012 09:23:05 -0400 Received: from aamtaout02-winn.ispmail.ntl.com ([81.103.221.35]) by mtaout01-winn.ispmail.ntl.com (InterMail vM.7.08.04.00 201-2186-134-20080326) with ESMTP id <20120628132301.DGMX3118.mtaout01-winn.ispmail.ntl.com@aamtaout02-winn.ispmail.ntl.com>; Thu, 28 Jun 2012 14:23:01 +0100 Received: from p2 ([82.0.126.133]) by aamtaout02-winn.ispmail.ntl.com (InterMail vG.3.00.04.00 201-2196-133-20080908) with ESMTP id <20120628132301.VGUS3795.aamtaout02-winn.ispmail.ntl.com@p2>; Thu, 28 Jun 2012 14:23:01 +0100 To: "'Nikita Popov'" , "'PHP internals'" Date: Thu, 28 Jun 2012 14:24:35 +0100 MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Mailer: Microsoft Office Outlook, Build 11.0.5510 In-reply-to: Thread-Index: Ac1VHVYR1lcf4efySHe8p2Vdl6sxqAAES1Gw X-MimeOLE: Produced By Microsoft MimeOLE V6.00.3790.4325 Message-ID: <20120628132301.VGUS3795.aamtaout02-winn.ispmail.ntl.com@p2> X-Cloudmark-Analysis: v=1.1 cv=R50lirqlHffDPPkwUlkuVa99MrvKdVWo//yz83qex8g= c=1 sm=0 a=y_ar4ihSIBsA:10 a=uObrxnre4hsA:10 a=oHf_AeXmDeQA:10 a=kj9zAlcOel0A:10 a=pGLkceISAAAA:8 a=NEAV23lmAAAA:8 a=t5hj8CK87xUwSjlkbeQA:9 a=CjuIK1q_8ugA:10 a=MSl-tDqOz04A:10 a=HpAAvcLHHh0Zw7uRqdWCyQ==:117 Subject: RE: [PHP-DEV] List comprehensions and generator expressions for PHP From: jared.williams1@ntlworld.com ("Jared Williams") References: > -----Original Message----- > From: Nikita Popov [mailto:nikita.ppv@gmail.com] > Sent: 28 June 2012 11:49 > To: PHP internals > Subject: [PHP-DEV] List comprehensions and generator > expressions for PHP > > 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? No mention of yielding keys in the comprehensions. Presume that would work? I would definitely like to see generators in, the amount of boilerplate code required for the current Iterator interface is not nice. Also have been playing with your addGeneratorsSupport branch, trying to see If can get any benefits from using asynchronous operations. The simplest example I could think of using Memcached (assuming libmemcached fetch() does return as soon as it recieves an item) Eg. function *getMulti(Memcached $memcached, array $keys) { if ($memcached->getDelayed($keys)) { while ($item = $memcached->fetch()) yield $item['key'] => $item['value']; } } foreach(getMulti($memcached, ['foo', 'bar', ... ]) as $key => $value) { doSomeWork(); } So doSomeWork would be called as soon as a single value is available, rather than having to wait til all values have returned. Should in theory work right? Jared