higher-order funcs for ranges (with usual interface)
Lars T. Kyllingstad
public at kyllingen.NOSPAMnet
Wed Feb 2 23:41:40 PST 2011
On Wed, 02 Feb 2011 18:38:02 +0100, spir wrote:
> 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?
I'm not sure I understand what you mean here. Perhaps you're looking for
something like concepts, which have been discussed for both D and C++0x
but rejected in both languages:
http://en.wikipedia.org/wiki/Concept_%28generic_programming%29
Anyway, if the source and target range are of the same (known) kind,
something like this should work:
struct MyRange(T) { ... }
MyRange!Out map(In, Out)(MyRange!In input, Out delegate(In) f)
{
...
}
If they are of different kinds, but still known, this should work:
struct MySourceRange(T) { ... }
struct MyTargetRange(T) { ... }
MyTargetRange!Out map(In, Out)
(MySourceRange!In input, Out delegate(In) f)
{
...
}
Note that I am only talking about what the compiler should be able to
figure out through IFTI (implicit function template instantiation), and
not about actual implementation.
>> 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.
map() is just a helper function. Unlike struct literals/constructors,
functions can make use of IFTI, which makes for much prettier code:
// The range from my example, without map()
auto result =
Map!(SomeRange!int, int, bool)(someRange, someDelegate);
// The range from my example, with map()
auto result = map(someInputRange, someDelegate);
This has become a quite common idiom in Phobos. std.range, for instance,
is littered with helper functions like this:
Retro, retro()
Stride, stride()
Chain, chain()
...
The list goes on.
-Lars
More information about the Digitalmars-d-learn
mailing list