
On 22/12/2010 06:40, Brent Spillner wrote:
* Lazy evaluation of every comprehension. In contrast, boost::filter_iterator applies the filter predicate to the entire wrapped range at construction time (via satisfy_predicate().)
That's incorrect. satisfy_predicate() only advances the inner iterator until the first element that satisfies the predicate is found, so as to skip the elements that do not satisfy it. It is indeed called at construction time, and also at each increment. Doing it some other way (advancing in decrement) would lead to more branches and data than is necessary.
* Predictable backtracking behavior that allows filter criteria to depend on each other. This enables a number of computations (e.g. looping over the elements within a range of ranges) that are otherwise difficult or impossible to express, and even in straightforward cases permits some efficiency improvements. For instance, your example Pythagorean triple comprehension using Boost.Range evaluates a number of combinations of the three indices equal to the n'th cubic number, while the 'i<<= range(1,n), j<<= range(i,n), k<<=range(j,n)' version evaluates the n'th tetrahedral number's worth of combinations. The ratio of these quantities approaches 6 as n increases, and is in fact greater that 5 by n=16. A factor of 5 speedup is surely worth something.
I hope this backtracking can be efficiently implemented. I'll have to take a look at your implementation.
* Somewhat more concise and I believe more readable syntax. My goal was to require fewer characters than a Python comprehension and ideally not many more than Haskell, and I think I'm there in the simple cases. (Neglecting the variable declarations, naturally.)
You could predeclare a set of variables in a specific namespace to reduce that boilerplate to a certain point.
I've never used Phoenix; it seems fascinating but I'm a little wary of making it a dependency just to get something simple like a comprehension syntax.
It shouldn't matter at all what is being used. You should really allow any function object to be passed to define your criteria. Phoenix is just a powerful way to create function objects in an inline fashion. You just have to make sure the type you use for x, y, z etc. is compatible with Phoenix.
I also took a look at using Boost.Proto to provide the expression trees, but it seemed that the syntax was a little more verbose than it needs to be for things that will be evaluated only in the context of a comprehension expression.
Using Proto would mean defining a grammar for your syntax, and defining a transform that converts a C++ AST into the desired C++ code, here a lazily evaluated range. It's practical for various reasons: maintenance, validation, reusability, and better interoperability with other embedded languages defined using Proto.