Choice ranges?
Artur Skawina
art.08.09 at gmail.com
Fri Mar 28 17:37:42 PDT 2014
On 03/28/14 23:10, H. S. Teoh wrote:
> On Fri, Mar 28, 2014 at 10:44:11PM +0100, Artur Skawina wrote:
>> On 03/28/14 20:00, H. S. Teoh wrote:
>>> Today I ran into an interesting situation where I have a function f
>>> that needs to return ranges of different types (but identical
>>> element types):
>>>
>>> auto f(A...)(A args) {
>>> ...
>>> if (someCondition)
>>> return cartesianProduct(x, y)
>>> .joiner;
>>> else
>>> return cartesianProduct(x, y)
>>> .joiner
>>> .filter!someFilter;
>>> }
>>>
>>> This obviously can't compile, because the return types are not the
>>> same. (Note that someCondition is only known at runtime.) But
>>> abstractly speaking, it *should* work, because the element type of
>>> the returned range is identical.
>>>
>>> So how would I implement something like this?
>>
>> eg
>> auto f(A...)(A args) {
>> ...
>> return cartesianProduct(x, y)
>> .joiner
>> .filter!(a=>somecondition?true:somefilter(a));
>> }
> [...]
>
> It's not that simple, though. I guess I gave a bad example. In the
> actual code, someCondition decides whether or not a cartesian product is
> even used. So the return range needs to somehow switch between a
> cartesianProduct filtered by some filters, vs. an alternative algorithm
> filtered by some other filters.
D is statically typed -- there is no way for one function to return
different types that are selected by a runtime condition.
The fact that the produced elements have the same type is irrelevant.
You can wrap the range in a common one. When the above approach isn't
enough, there's always the next level:
struct DynRange(R, F...) {
R r;
/*immutable*/ size_t n;
static _ugh() { string r; foreach (N, _; F) r~="typeof(F["~N.stringof~"](r)) f"~N.stringof~";"; return r; }
union U { mixin(_ugh()); }
U u;
this(R r, size_t n) {
this.r = r; this.n = n;
foreach (N, _; typeof(F)) if (N==n) u.tupleof[N] = F[N](r);
}
auto ref front() @property { foreach (N, _; typeof(F)) if (N==n) return u.tupleof[N].front; assert(0); }
bool empty() @property { foreach (N, _; typeof(F)) if (N==n) return u.tupleof[N].empty; assert(0); }
void popFront() { foreach (N, _; typeof(F)) if (N==n) u.tupleof[N].popFront(); }
}
import std.array;
auto f(R)(R r, bool somecondition) {
import std.algorithm, std.conv;
return DynRange!(R,
map!"a*a",
r=>filter!"a%2"((map!(a=>to!string(a^^a).length)(r)))
)(r, somecondition);
}
void main() {
import std.stdio;
auto r = [1,2,3,4,5].f(false);
writeln(r);
r = [1,2,3,4,5].f(true);
writeln(r);
}
[Optimizing DynRange is left as an exercise for the reader. ;) In practice,
when used with just two chains, the gain would be minimal anyway. Adding
a level of indirection for the range primitives would prevent inlining.]
artur
More information about the Digitalmars-d-learn
mailing list