
I uploaded sample code to the Vault (in the "Miscellaneous/" directory) that provides a concise notation for list comprehensions. The comprehensions are evaluated lazily and can be used to populate a std::list, vector, or deque, appended to the same, inserted after a given iterator, or interpreted as a boost::range, including compatibility with BOOST_FOREACH. Containers, iterator-defined ranges, or nullary function objects can be used as generators, and arbitrary function objects can be used to specify filter conditions. For example, the Pythagorean Triples example from http://rosettacode.org/wiki/List_comprehensions can be coded as std::vector< boost::tuple<int,int,int> > triples =_(x, y, z)[x <<= range(1, n), y <<= range(x, n), z <<= range(y, n), x*x + y*y == z*z]; , and the (toy) prime number generator at http://www.secnetix.de/olli/Python/list_comprehensions.hawk can be implemented as std::vector<int> nonprimes = _(j)[i <<= range(2, sqrt_max), j <<= range(2 * i, sqrt_max * sqrt_max, i)]; boost::function<std::vector<int>::const_iterator ()> is_composite = bind(std::find<std::vector<int>::const_iterator, int>, nonprimes.begin(), nonprimes.end(), ref(p)); BOOST_FOREACH(int x, (_(p)[p <<= range(2, sqrt_max * sqrt_max), call(is_composite) == nonprimes.end()])) { std::cout << x << std::endl; } (Note the use of call() to represent delayed invocation, and the extra () around the comprehension when used with BOOST_FOREACH.) I've tested this so far with GCC 4.4.4, GCC 4.5.2, and Clang 2.9, using Boost 1.42 and 1.45, and with and without -std=c++0x. Reports of success/failure with other environments are most welcome. The code is still in a prototype stage, so I'm more than happy to take design suggestions and would love to hear about use cases I've overlooked. If there's sufficient interest I will work on getting it to production quality and writing some real documentation. I apologize in advance for wasting everybody's time if something like this already exists in Phoenix; I'm not too familiar with it but I skimmed the docs and didn't see anything. Enjoy, Brent

On 19/12/10 19:40, Brent Spillner wrote:
I uploaded sample code to the Vault (in the "Miscellaneous/" directory) that provides a concise notation for list comprehensions. The comprehensions are evaluated lazily and can be used to populate a std::list, vector, or deque, appended to the same, inserted after a given iterator, or interpreted as a boost::range, including compatibility with BOOST_FOREACH. Containers, iterator-defined ranges, or nullary function objects can be used as generators, and arbitrary function objects can be used to specify filter conditions.
I've tested this so far with GCC 4.4.4, GCC 4.5.2, and Clang 2.9, using Boost 1.42 and 1.45, and with and without -std=c++0x. Reports of success/failure with other environments are most welcome. The code is still in a prototype stage, so I'm more than happy to take design suggestions and would love to hear about use cases I've overlooked. If there's sufficient interest I will work on getting it to production quality and writing some real documentation. I apologize in advance for wasting everybody's time if something like this already exists in Phoenix; I'm not too familiar with it but I skimmed the docs and didn't see anything. I like the idea :) However, how did you manage the lazy part ? Do you roll your own lazy stuff or do you rely on proto ?
If the former, I encourage you to jump the agte and do the latter :)

At Sun, 19 Dec 2010 13:40:42 -0500, Brent Spillner wrote:
I uploaded sample code to the Vault (in the "Miscellaneous/" directory) that provides a concise notation for list comprehensions. The comprehensions are evaluated lazily and can be used to populate a std::list, vector, or deque, appended to the same, inserted after a given iterator, or interpreted as a boost::range, including compatibility with BOOST_FOREACH. Containers, iterator-defined ranges, or nullary function objects can be used as generators, and arbitrary function objects can be used to specify filter conditions.
I love it!!
For example, the Pythagorean Triples example from http://rosettacode.org/wiki/List_comprehensions can be coded as
std::vector< boost::tuple<int,int,int> > triples =_(x, y, z)[x <<= range(1, n), y <<= range(x, n), z <<= range(y, n), x*x + y*y == z*z];
, and the (toy) prime number generator at http://www.secnetix.de/olli/Python/list_comprehensions.hawk can be implemented as
std::vector<int> nonprimes = _(j)[i <<= range(2, sqrt_max), j <<= range(2 * i, sqrt_max * sqrt_max, i)];
boost::function<std::vector<int>::const_iterator ()> is_composite = bind(std::find<std::vector<int>::const_iterator, int>, nonprimes.begin(), nonprimes.end(), ref(p));
Not sure the above is portable, FWIW. Last I recall you can't take the address of standard library functions (because of the possibility of overloads causing ambiguity).
BOOST_FOREACH(int x, (_(p)[p <<= range(2, sqrt_max * sqrt_max), call(is_composite) == nonprimes.end()])) { std::cout << x << std::endl; }
I'm usually interested in comprehensions like (in Python): [ x, foo(x) for x in some_other_sequence ] and your consistent use of range() above obscures whether "for x in some_other_sequence" can be modeled in general. Can I substitute any sequence for range(...) in the above examples? Anyway, very promising. Keep up the great work! -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On 12/19/10 12:40, Brent Spillner wrote:
I uploaded sample code to the Vault (in the "Miscellaneous/" directory) that provides a concise notation for list comprehensions. The comprehensions are evaluated lazily and can be used to populate a std::list, vector, or deque, appended to the same, inserted after a given iterator, or interpreted as a boost::range, including compatibility with BOOST_FOREACH. Containers, iterator-defined ranges, or nullary function objects can be used as generators, and arbitrary function objects can be used to specify filter conditions.
List comprehensions in haskell can be done with monads: http://book.realworldhaskell.org/read/monads.html Why not 1st define a c++ monad and then define the list comprehension using the moand? If that were done, then boost would have both. Actually, fcpp already has it in monad.h. Fcpp is available at sourceforge: http://sourceforge.net/projects/fcpp/ However, I've not used fcpp and several years ago boost reviewed it but decided not to include it. http://lists.boost.org/Archives/boost/2004/02/61840.php However, I think defining a monad acceptable to boost would be useful since it is used extensively in functional programming. -regards, Larry

On 12/19/10 19:54, Larry Evans wrote: [snip]
Actually, fcpp already has it in monad.h. Fcpp is available at sourceforge:
[snip] OOPS, That doesn't look like it's maintained and doesn't have any test drivers or examples. This location might be more complete: http://www.cc.gatech.edu/~yannis/fc++/download.html One of the authors web page is now here: http://www.cs.umass.edu/~yannis/

On 12/19/10 22:33, Larry Evans wrote:
On 12/19/10 19:54, Larry Evans wrote: [snip]
Actually, fcpp already has it in monad.h. Fcpp is available at sourceforge:
[snip] OOPS, That doesn't look like it's maintained and doesn't have any test drivers or examples. This location might be more complete:
[snip] Brent, Using the fc++ library, the attached c++ program produces output: --{-- output --- make -k run /home/evansl/prog_dev/boost-svn/ro/trunk/sandbox-local/build/gcc4_4n/boost-svn/ro/trunk/sandbox-local/fc++/sourceforge/sandbox/monad_list_comprehension.exe ---------:41 1,3 1,4 2,3 2,4 --------- Compilation finished at Mon Dec 20 04:28:48 --}-- output --- Actually, the following line(around line 587): typedef typename OrigET::template Go<Dummy>::BE BE1; had to be removed from the fc++ lambda.h file to enable compilation of the attached. Maybe you could merge the best ideas from both your and fc++'s version of list comprehension. Anyway, I thought it might be useful to see how another list comprehension was implemented. -regards, Larry

On 12/20/10 04:37, Larry Evans wrote:
On 12/19/10 22:33, Larry Evans wrote:
On 12/19/10 19:54, Larry Evans wrote: [snip]
Actually, fcpp already has it in monad.h. Fcpp is available at sourceforge:
[snip] OOPS, That doesn't look like it's maintained and doesn't have any test drivers or examples. This location might be more complete:
[snip]
Brent,
Using the fc++ library, the attached c++ program produces output:
OOPS (again) ;) I'd posted this same code to this list a while back: http://article.gmane.org/gmane.comp.lib.boost.devel/190970 in a thread on the same topic. I did email one of the FC++ authors back then; however, he said:
I am not that familiar with the FC++ monad code myself
so, maybe gathering ideas from FC++ wouldn't be that productive, but who knows. -Larry

Dave Abrahams wrote:
I'm usually interested in comprehensions like (in Python):
[ x, foo(x) for x in some_other_sequence ]
and your consistent use of range() above obscures whether "for x in some_other_sequence" can be modeled in general. Can I substitute any sequence for range(...) in the above examples?
Yes, sorry for not making that clear. Continuing the prime number example, you can generate Mersenne primes with std::vector<int> mersenne_primes = _((1 << p) - 1)(7)[p <<= primes, apply(is_prime, (1 << p) - 1)]; This illustrates several points that I neglected before-- use of apply() to invoke a function object with arguments, specification of a finite limit (7) on the number of values to be returned (assigning to a container via <<= would also implicitly limit it to the size of that container), and the fact that I haven't yet allowed any assignment or side-effect (increment/decrement) operators within the comprehension body. My initial instinct was that that wouldn't be very FP-ish, but now I'm thinking it would be handier to allow things like 'm = (1 << p) - 1' or 'count++' within the condition list. I uploaded a new version to the Vault that includes the Mersenne generator example and a few bugfixes.
Anyway, very promising. Keep up the great work!
Thanks, I appreciate the encouragement! Brent

On 19/12/2010 19:40, Brent Spillner wrote:
I uploaded sample code to the Vault (in the "Miscellaneous/" directory) that provides a concise notation for list comprehensions. The comprehensions are evaluated lazily and can be used to populate a std::list, vector, or deque, appended to the same, inserted after a given iterator, or interpreted as a boost::range, including compatibility with BOOST_FOREACH. Containers, iterator-defined ranges, or nullary function objects can be used as generators, and arbitrary function objects can be used to specify filter conditions.
Can you explain the difference with the Boost.Range adaptors?
For example, the Pythagorean Triples example from http://rosettacode.org/wiki/List_comprehensions can be coded as
std::vector< boost::tuple<int,int,int> > triples =_(x, y, z)[x<<= range(1, n), y<<= range(x, n), z<<= range(y, n), x*x + y*y == z*z];
Here is the approximation of this with Boost.Range : auto r = combine(irange(1, n), irange(1, n), irange(1, n)) | filtered(at_c<0>(_1)*at_c<0>(_1) + at_c<1>*at_c<1> == at_c<2>*at_c<2>); std::vector< boost::tuple<int, int, int> > triples(r.begin(), r.end()); It doesn't support tuples with an easy syntax like you do, and there is no way to define dependencies between the tuple elements. It is true Boost.Range is quite lacking in terms of range generators, and maybe your solution is what we need. It would be nice however, if it didn't reinvent the wheel and can be based on Phoenix for the functional part.
participants (5)
-
Brent Spillner
-
Dave Abrahams
-
joel falcou
-
Larry Evans
-
Mathias Gaunard