higher-order funcs for ranges (with usual interface)

spir denis.spir at gmail.com
Wed Feb 2 09:38:02 PST 2011


On 02/02/2011 02:18 PM, Lars T. Kyllingstad wrote:
> On Wed, 02 Feb 2011 13:26:39 +0100, spir wrote:
>
>> Hello,
>>
>> This bit of code for arrays:
>>
>> Out[] map (In,Out) (In[] input, Out delegate (In) f) {
>>       Out[] output = new Out[](input.length); foreach (i,item ; input)
>>           output [i] = f(item);
>>       return output;
>> }
>> unittest {
>>       char character (uint code) {return cast(char)code;} uint[] codes =
>>       [0x61,0x62,0x63];
>>       // functional style
>>       writeln(map(codes,&character));    // "abc" // OO style
>>       writeln(codes.map(&character));     // "abc"
>> }
>>
>> How to write this for ranges? [...]
>>
>> For ranges, I'm looking for something similar to:
>>       Range!Out map (In,Out) (Range!In input, Out delegate (In) f) {...}
>> Indeed, the compiler should understand that Range!T is a type id just
>> like T[].
>
> I don't think it's possible to do it exactly as you describe.  I mean,
> Range in that case can be anything, and you can't always return a range
> of the same kind.

Right. The output range's ElementType is given by f's return type. As you say, 
the "kind" of range may change. Even if it's the same, how could one express 
that: <range_which-elements-are-of-type-T>, syntactically and in the param set, 
just like <array_which-elements-are-of-type-> is written "T[]"?
Currently, we must (1) declare the range type as template param, which is a bit 
redondant because the ElementType must also be given, (2) add some 'is' horror 
code:
      if (isInputRange!Range && is(ElementType!Range : In))
I guess the only solution would be for the compiler to support a kind of reange 
type syntax?

>   Two possibilities are, you can do it eagerly,
>
>    Out[] map(Range, In, Out)(Range input, Out delegate(In) f)
>        if (isInputRange!Range&&  is(ElementType!Range : In))
>    {
>        ...
>    }

OK.

> or you can do it lazily by defining your own map range (untested):
>
>    struct Map(Range, In, Out)
>        if (isInputRange!Range&&  is(ElementType!Range : In)
>    {
>        Range input;
>        Out delegate(In) f;
>
>        @property bool empty() { return input.empty; }
>
>        // Inefficient, should cache front...
>        @property Out front() { return f(input.front); }
>
>        void popFront() { input.popFront(); }
>    }

That's similar to what I did.

>    Map!(Range, Out) map(Range, In, Out)(Range input, Out delegate(In) f)
>        if (isInputRange!R&&  is(ElementType!Range : In)
>    {
>        return typeof(return)(input, f);
>    }

What's the point of map, then? My version initially had a 'MapRange' defined as 
static struct template inside map, but then map just instanciated it, so I 
suppressed map alltogether, letting the user write:
	auto r2 = MapRange!(R1, In, Out)(input, f);
which is not more complicated than calling the func, I guess.

> -Lars

Denis
-- 
_________________
vita es estrany
spir.wikidot.com



More information about the Digitalmars-d-learn mailing list