Re: Dynamic chain for ranges?
On Monday, 13 June 2022 at 14:03:13 UTC, Steven Schveighoffer wrote: Merge sort only works if it's easy to manipulate the structure, like a linked-list, or to build a new structure, like if you don't care about allocating a new array every iteration. The easiest option is to have two buffers that can hold all items, in the last merge you merge back to the input-storage. But yeah, it is only «fast» for very large arrays.
Re: Dynamic chain for ranges?
On 6/13/22 9:44 AM, Ola Fosheim Grøstad wrote: On Monday, 13 June 2022 at 13:22:52 UTC, Steven Schveighoffer wrote: I would think sort(joiner([arr1, arr2, arr3])) should work, but it's not a random access range. Yes, I got the error «must satisfy the following constraint: isRandomAccessRange!Range`». It would be relatively easy to make it work as a random access range if arr1, arr2, etc were fixed size slices. Or I guess, use insertion-sort followed by merge-sort. Merge sort only works if it's easy to manipulate the structure, like a linked-list, or to build a new structure, like if you don't care about allocating a new array every iteration. -Steve
Re: Dynamic chain for ranges?
On Monday, 13 June 2022 at 13:22:52 UTC, Steven Schveighoffer wrote: I would think sort(joiner([arr1, arr2, arr3])) should work, but it's not a random access range. Yes, I got the error «must satisfy the following constraint: isRandomAccessRange!Range`». It would be relatively easy to make it work as a random access range if arr1, arr2, etc were fixed size slices. Or I guess, use insertion-sort followed by merge-sort.
Re: Dynamic chain for ranges?
On 6/13/22 4:51 AM, Ola Fosheim Grøstad wrote: Is there a dynamic chain primitive, so that you can add to the chain at runtime? Context: the following example on the front page is interesting. ```d void main() { int[] arr1 = [4, 9, 7]; int[] arr2 = [5, 2, 1, 10]; int[] arr3 = [6, 8, 3]; sort(chain(arr1, arr2, arr3)); writefln("%s\n%s\n%s\n", arr1, arr2, arr3); } ``` But it would be much more useful in practice if "chain" was a dynamic array. `chain` allows ranges of different types. `joiner` should be the equivalent for a dynamic range of ranges of the same type. I would think sort(joiner([arr1, arr2, arr3])) should work, but it's not a random access range. Most likely because the big-O constants are no longer constant. -Steve
Re: Dynamic chain for ranges?
On Monday, 13 June 2022 at 09:08:40 UTC, Salih Dincer wrote: On Monday, 13 June 2022 at 08:51:03 UTC, Ola Fosheim Grøstad wrote: But it would be much more useful in practice if "chain" was a dynamic array. Already so: I meant something like: chain = [arr1, arr2, …, arrN] I don't use ranges, but I thought this specific use case could be valuable. Imagine you have a chunked datastructure of unknown lengths, and you want to "redistribute" items without reallocation.
Re: Dynamic chain for ranges?
On Monday, 13 June 2022 at 08:51:03 UTC, Ola Fosheim Grøstad wrote: But it would be much more useful in practice if "chain" was a dynamic array. Already so: ```d int[] arr = [4, 9, 7, 5, 2, 1, 10, 6, 8, 3]; int[] arr1 = arr[0..3]; int[] arr2 = arr[3..7]; int[] arr3 = arr[7..$]; sort(chain(arr1, arr2, arr3)); writefln("%s\n%s\n%s\n", arr1, arr2, arr3); typeid(arr).writeln(": ", arr); writeln(&arr[0], " == ", &arr1[0]); /* Print Out: [1, 2, 3] [4, 5, 6, 7] [8, 9, 10] int[]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 7F7FBE348000 == 7F7FBE348000 */ ```
Dynamic chain for ranges?
Is there a dynamic chain primitive, so that you can add to the chain at runtime? Context: the following example on the front page is interesting. ```d void main() { int[] arr1 = [4, 9, 7]; int[] arr2 = [5, 2, 1, 10]; int[] arr3 = [6, 8, 3]; sort(chain(arr1, arr2, arr3)); writefln("%s\n%s\n%s\n", arr1, arr2, arr3); } ``` But it would be much more useful in practice if "chain" was a dynamic array.
Re: bool empty() const for ranges
On 11/26/21 5:44 AM, Salih Dincer wrote: Hi All; I have two questions that make each other redundant. Please answer one of them. I'm implementing ```bool empty() const``` for ranges as below: ```d bool empty() // const { bool result; if(!head) { result = true; fastRewind(); } return result; // head ? false : true; } ``` * Is the const essential for ranges? No, there is no specification of whether any range methods have to be const. As Stanislav says, it is only a requirement that subsequent calls return the same value as long as popFront has not been called. * Is it possible to rewind the pointer (```Node * head;```) when my head is empty by the const? If const is set, then any members are treated as const, and anything they point to are treated as const. So no. That being said, if `fastRewind` changes `empty` from true to false, the method is invalid. -Steve
Re: bool empty() const for ranges
On Friday, 26 November 2021 at 10:44:10 UTC, Salih Dincer wrote: * Is the const essential for ranges? * Is it possible to rewind the pointer (```Node * head;```) when my head is empty by the const? `empty` is not required to be `const`, but it is required to yield the same result if called multiple times without mutating the range (see https://dlang.org/phobos/std_range_primitives.html#.isInputRange). In other words, you really ought not to mutate the range in implementation of `empty`, or at least not to the extent you seem to want to.
bool empty() const for ranges
Hi All; I have two questions that make each other redundant. Please answer one of them. I'm implementing ```bool empty() const``` for ranges as below: ```d bool empty() // const { bool result; if(!head) { result = true; fastRewind(); } return result; // head ? false : true; } ``` * Is the const essential for ranges? * Is it possible to rewind the pointer (```Node * head;```) when my head is empty by the const? Thanks...
Re: Idiomatic error handling for ranges
On Thursday, 5 April 2018 at 17:36:56 UTC, Seb wrote: On Thursday, 5 April 2018 at 17:06:04 UTC, rumbu wrote: Is there a standard way to handle errors in a chain of range transformations? [...] Are you aware of ifThrown? https://dlang.org/phobos/std_exception.html#ifThrown It's not perfect, but imho a nice start and one of the places where lazy really shines. Thanks, ifThrown is perfect.
Re: Idiomatic error handling for ranges
On Thursday, 5 April 2018 at 17:06:04 UTC, rumbu wrote: Is there a standard way to handle errors in a chain of range transformations? [...] Are you aware of ifThrown? https://dlang.org/phobos/std_exception.html#ifThrown It's not perfect, but imho a nice start and one of the places where lazy really shines.
Re: Idiomatic error handling for ranges
On Thursday, 5 April 2018 at 17:06:04 UTC, rumbu wrote: Is there a standard way to handle errors in a chain of range transformations? Let's say I want to read some comma separated numbers from a file. auto myArray = file.byLine().splitter().map!(to!int).array(); Now, besides fatal errors (like I/O), let's suppose I want to handle some errors in a silent way: - skip unicode decoding errors; - assume blank records with 0; - skip the line entirely if the conversion to int is not possible; I can catch UTFException, ConvException or ConvOverflowException for the entire syntax chain but there are some disadvantages: - I don't know exactly which of the chain members thrown the exception; - I cannot skip/handle the error and continue; The only solution I thought about is to break the nice chain syntax and handle errors on each of the chain members, but I wonder if there is probably another way. Thanks. You could use predicates: ``` string list = "3, 5, 1, , not a number, 100"; int[] numbers = list.split(",").filter!((entry) { if (!isNumeric(entry.strip)) return false; else // ... even more cases? return true; }) .map!((e) => e.strip.to!int).array; assert(numbers == [3, 5, 1, 100]); ``` https://run.dlang.io/gist/413282d9726dbac137bf5f35033a8eea
Idiomatic error handling for ranges
Is there a standard way to handle errors in a chain of range transformations? Let's say I want to read some comma separated numbers from a file. auto myArray = file.byLine().splitter().map!(to!int).array(); Now, besides fatal errors (like I/O), let's suppose I want to handle some errors in a silent way: - skip unicode decoding errors; - assume blank records with 0; - skip the line entirely if the conversion to int is not possible; I can catch UTFException, ConvException or ConvOverflowException for the entire syntax chain but there are some disadvantages: - I don't know exactly which of the chain members thrown the exception; - I cannot skip/handle the error and continue; The only solution I thought about is to break the nice chain syntax and handle errors on each of the chain members, but I wonder if there is probably another way. Thanks.
Re: for ranges
On 1/23/15 4:44 AM, ixid wrote: On Thursday, 22 January 2015 at 16:41:49 UTC, Russel Winder wrote: Playing with factorial implementations, as you do. I had a D implementation using ulong. Not sensible obviously since overflow is a bit of a problem. But the code worked, as did the tests. Now converting to BigInt and… The standard explicit iteration form uses a loop: for(i; 2..n+1) for n = 0 or 1 this loop doesn't loop since the range is [,). However for BigInt: for(i; two..n + one) the loop starts at 0 and just keeps on going. This is clearly not good. Am I having a mental breakdown or is this a real bug? In general it feels as if BigInt needs more work as it doesn't work with simple generic code in too many cases. Templates get confused by invocation with a literal and a BigInt for example when it should have a single type. Literals feel too strongly typed or too weakly implicitly convertible. This is not a BigInt problem, it's an inconsistency with foreach range. See my earlier post. -Steve
Re: for ranges
On Thursday, 22 January 2015 at 16:41:49 UTC, Russel Winder wrote: Playing with factorial implementations, as you do. I had a D implementation using ulong. Not sensible obviously since overflow is a bit of a problem. But the code worked, as did the tests. Now converting to BigInt and… The standard explicit iteration form uses a loop: for(i; 2..n+1) for n = 0 or 1 this loop doesn't loop since the range is [,). However for BigInt: for(i; two..n + one) the loop starts at 0 and just keeps on going. This is clearly not good. Am I having a mental breakdown or is this a real bug? In general it feels as if BigInt needs more work as it doesn't work with simple generic code in too many cases. Templates get confused by invocation with a literal and a BigInt for example when it should have a single type. Literals feel too strongly typed or too weakly implicitly convertible.
Re: for ranges
On 1/22/15 11:41 AM, Russel Winder via Digitalmars-d-learn wrote: Playing with factorial implementations, as you do. I had a D implementation using ulong. Not sensible obviously since overflow is a bit of a problem. But the code worked, as did the tests. Now converting to BigInt and… The standard explicit iteration form uses a loop: for(i; 2..n+1) for n = 0 or 1 this loop doesn't loop since the range is [,). However for BigInt: for(i; two..n + one) the loop starts at 0 and just keeps on going. This is clearly not good. Am I having a mental breakdown or is this a real bug? The issue: import std.stdio; struct S { int x; int opCmp(const S other) const { writeln("compare"); return x < other.x ? -1 : x > other.x ? 1 : 0;} bool opEquals(const S other) const { writeln("equals"); return x == other.x;} ref S opOpAssign(string op)(int other) { mixin("x " ~ op ~ "= other;"); return this; } } void main() { immutable S one = S(1); immutable S two = S(2); foreach(s; one..two) {} } output: equals equals So foreach(S; one..two) translates to: for(S x = one; x != two; x += 1) which explains the infinite loop. I'm almost positive foreach(x; 1..2) uses comparison instead of equality to check for end condition. -Steve
Re: for ranges
On Thu, 2015-01-22 at 16:48 +, bearophile via Digitalmars-d-learn wrote: > > It works for me: > > Sorry, you are right, it loops: So it is a bug, and I now have to find out how to report it! -- Russel. = Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.win...@ekiga.net 41 Buckmaster Roadm: +44 7770 465 077 xmpp: rus...@winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder signature.asc Description: This is a digitally signed message part
Re: for ranges
Russel Winder: However for BigInt: for(i; two..n + one) the loop starts at 0 and just keeps on going. This is clearly not good. It works for me: void main() { import std.stdio, std.bigint; immutable BigInt one = 1; immutable BigInt two = 2; uint n = 100; foreach (immutable i; two .. n + one) i.writeln; } Bye, bearophile
Re: for ranges
It works for me: Sorry, you are right, it loops: void main() { import std.stdio, std.bigint; immutable BigInt one = 1; immutable BigInt two = 2; uint n = 0; foreach (immutable i; two .. n + one) i.writeln; } Bye, bearophile
for ranges
Playing with factorial implementations, as you do. I had a D implementation using ulong. Not sensible obviously since overflow is a bit of a problem. But the code worked, as did the tests. Now converting to BigInt and… The standard explicit iteration form uses a loop: for(i; 2..n+1) for n = 0 or 1 this loop doesn't loop since the range is [,). However for BigInt: for(i; two..n + one) the loop starts at 0 and just keeps on going. This is clearly not good. Am I having a mental breakdown or is this a real bug? -- Russel. = Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.win...@ekiga.net 41 Buckmaster Roadm: +44 7770 465 077 xmpp: rus...@winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder signature.asc Description: This is a digitally signed message part
Re: Defining inout returned values for ranges
On Thu, 05 Sep 2013 23:32:10 +0200, anonymous wrote: > On Thursday, 5 September 2013 at 19:19:42 UTC, Jonathan Crapuchettes > wrote: >> On Wed, 04 Sep 2013 11:49:41 +0200, anonymous wrote: > [...] >>> You can use a Template This Parameter [1] instead of inout: >>> >>> auto opSlice(this This)() >>> { >>> static if(is(This == const)) alias QualifiedT = const >>> T; >>> else alias QualifiedT = T; >>> >>> static struct Range { >>> QualifiedT front() @property { >>> >>> >>> [1] http://dlang.org/template.html#TemplateThisParameter >> >> Thank you for the help. That worked, but now I ran into another similar >> issue. What if the Range struct is being defined outside of the opSlice >> method? I have a number of methods in a struct that return the same >> range type and would rather not have to have duplicate code or need to >> use mixins. >> >> JC > > Templatize Range and pass it This: > > static struct Range(This) > { > static if(is(This == const)) alias QualifiedT = const T; > else alias QualifiedT = T; > > QualifiedT front() @property {...} > } > > auto opSlice(this This)() > { > return Range!This(); > } > > ... more methods in the style of opSlice ... > > > If you need QualifiedT in opSlice, make it a template over This, > too. Thank you again. It all appears to be working correctly. JC
Re: Defining inout returned values for ranges
On Thursday, 5 September 2013 at 19:19:42 UTC, Jonathan Crapuchettes wrote: On Wed, 04 Sep 2013 11:49:41 +0200, anonymous wrote: [...] You can use a Template This Parameter [1] instead of inout: auto opSlice(this This)() { static if(is(This == const)) alias QualifiedT = const T; else alias QualifiedT = T; static struct Range { QualifiedT front() @property { [1] http://dlang.org/template.html#TemplateThisParameter Thank you for the help. That worked, but now I ran into another similar issue. What if the Range struct is being defined outside of the opSlice method? I have a number of methods in a struct that return the same range type and would rather not have to have duplicate code or need to use mixins. JC Templatize Range and pass it This: static struct Range(This) { static if(is(This == const)) alias QualifiedT = const T; else alias QualifiedT = T; QualifiedT front() @property {...} } auto opSlice(this This)() { return Range!This(); } ... more methods in the style of opSlice ... If you need QualifiedT in opSlice, make it a template over This, too.
Re: Defining inout returned values for ranges
On Wed, 04 Sep 2013 11:49:41 +0200, anonymous wrote: > On Wednesday, 4 September 2013 at 00:56:39 UTC, Jonathan Crapuchettes > wrote: >> If a range struct (Range) is defined inside another struct (Test), how >> can the constness or mutability of Test be attributed to the return >> type of Range.front? I'm running into this problem because I need the >> range to be iterated, but I need the pointer in T to be marked const >> when appropriate. >> >> Thank you, >> JC >> >> Pseudo-Example: >> struct Test { >> static struct T { >> uint* ptr; >> } >> ... >> >> auto opSlice() inout { >> static struct Range { >> inout(T) front() @property { >> ... >> } >> ... >> } >> >> return Range(); >> } >> } > > > You can use a Template This Parameter [1] instead of inout: > > auto opSlice(this This)() > { > static if(is(This == const)) alias QualifiedT = const T; > else alias QualifiedT = T; > > static struct Range { > QualifiedT front() @property { > > > [1] http://dlang.org/template.html#TemplateThisParameter Thank you for the help. That worked, but now I ran into another similar issue. What if the Range struct is being defined outside of the opSlice method? I have a number of methods in a struct that return the same range type and would rather not have to have duplicate code or need to use mixins. JC
Re: Defining inout returned values for ranges
On Wednesday, 4 September 2013 at 00:56:39 UTC, Jonathan Crapuchettes wrote: If a range struct (Range) is defined inside another struct (Test), how can the constness or mutability of Test be attributed to the return type of Range.front? I'm running into this problem because I need the range to be iterated, but I need the pointer in T to be marked const when appropriate. Thank you, JC Pseudo-Example: struct Test { static struct T { uint* ptr; } ... auto opSlice() inout { static struct Range { inout(T) front() @property { ... } ... } return Range(); } } You can use a Template This Parameter [1] instead of inout: auto opSlice(this This)() { static if(is(This == const)) alias QualifiedT = const T; else alias QualifiedT = T; static struct Range { QualifiedT front() @property { [1] http://dlang.org/template.html#TemplateThisParameter
Defining inout returned values for ranges
If a range struct (Range) is defined inside another struct (Test), how can the constness or mutability of Test be attributed to the return type of Range.front? I'm running into this problem because I need the range to be iterated, but I need the pointer in T to be marked const when appropriate. Thank you, JC Pseudo-Example: struct Test { static struct T { uint* ptr; } ... auto opSlice() inout { static struct Range { inout(T) front() @property { ... } ... } return Range(); } }
Re: foreach for ranges?
On 07/18/2012 08:21 AM, Mike L. wrote: >> Also, UFCS makes no sense on overloaded operators, because they don't get >> called with ".", and all UFCS is is changing obj.func(params) to >> func(obj, >> params). >> >> - Jonathan M Davis > > Ok, that's basically what I was wondering. I had assumed foreach(e; > someThing) {} could possibly have been converted to someThing.opApply() > . Thanks for clarifying. But that is still true and opApply receives the body of the foreach loop as a delegate: someThing.opApply(delegate int(/* loop variables */) { // ... the body of foreach ... return terminationCode; // whether the user did 'break;' }); Also, the following bug (that is already fixed) is somewhat related to this discussion: http://d.puremagic.com/issues/show_bug.cgi?id=5605 Ali
Re: foreach for ranges?
Also, UFCS makes no sense on overloaded operators, because they don't get called with ".", and all UFCS is is changing obj.func(params) to func(obj, params). - Jonathan M Davis Ok, that's basically what I was wondering. I had assumed foreach(e; someThing) {} could possibly have been converted to someThing.opApply() . Thanks for clarifying.
Re: foreach for ranges?
On Wednesday, July 18, 2012 07:19:59 Kapps wrote: > If UFCS worked on operators, you would be able to make ranges > without any compiler support. > > int opApply(T)(T Range) if(isInputRange!T) { > // Stuff. > } You can't overload operators externally to a user-defined type. They must be part of the user-defined type that they operate on. Also, UFCS makes no sense on overloaded operators, because they don't get called with ".", and all UFCS is is changing obj.func(params) to func(obj, params). - Jonathan M Davis
Re: foreach for ranges?
On Wednesday, 18 July 2012 at 04:54:51 UTC, Jonathan M Davis wrote: On Wednesday, July 18, 2012 06:27:28 Mike L. wrote: Thanks for the reply. Not sure how I missed it there. Interesting that the compiler has to be made aware of a concept that I had thought was only supposed to be part of phobos. Would it be possible to implement it in std.range using the new UFCs? You can do for(; !range.empty; range.popFront()) { auto e = range.front; } without the compiler doing anything at all. But if you want for(e; range) {} you need a way in the language for the compiler to translate that into the above. UFCS has nothing to do with it. All UFCS does is take a.b(c, d) and make it b(a, c, d). - Jonathan M Davi If UFCS worked on operators, you would be able to make ranges without any compiler support. int opApply(T)(T Range) if(isInputRange!T) { // Stuff. }
Re: foreach for ranges?
On Wednesday, July 18, 2012 06:27:28 Mike L. wrote: > Thanks for the reply. Not sure how I missed it there. Interesting > that the compiler has to be made aware of a concept that I had > thought was only supposed to be part of phobos. Would it be > possible to implement it in std.range using the new UFCs? You can do for(; !range.empty; range.popFront()) { auto e = range.front; } without the compiler doing anything at all. But if you want for(e; range) {} you need a way in the language for the compiler to translate that into the above. UFCS has nothing to do with it. All UFCS does is take a.b(c, d) and make it b(a, c, d). - Jonathan M Davi
Re: foreach for ranges?
Thanks for the reply. Not sure how I missed it there. Interesting that the compiler has to be made aware of a concept that I had thought was only supposed to be part of phobos. Would it be possible to implement it in std.range using the new UFCs? On Tuesday, 17 July 2012 at 19:17:47 UTC, Ali Çehreli wrote: On 07/17/2012 11:59 AM, Mike L. wrote: How exactly does the compiler know how to do foreach on ranges (for example, ones returned by std.algorithm.map ?) I've looked around in std.algorithm and std.range, but can't seem to figure it out. The spec mentions it under 'Foreach over Structs and Classes with Ranges': http://dlang.org/statement.html#ForeachStatement There are two methods: a) opApply() member functions (and opApplyReverse(), which is rumored to be deprecated in the future) b) empty, front, and popFront() member functions Ali
Re: foreach for ranges?
On Tuesday, 17 July 2012 at 19:45:45 UTC, Jonathan M Davis wrote: This post gives the current precedence, but there was some discussion of adjusting it a bit: http://forum.dlang.org/post/mailman.275.1342019430.31962.digitalmars- d...@puremagic.com From the looks of it, opApply gets precedence right now. But mixing ranges and opApply isn't a good idea in most cases, so it doesn't come up very often. - Jonathan M Davis Ok, thanks !
Re: foreach for ranges?
On Tuesday, July 17, 2012 21:38:51 Eyyub wrote: > On Tuesday, 17 July 2012 at 19:27:54 UTC, Jonathan M Davis wrote: > > It translates > > > > foreach(e; range) > > {} > > > > into something like > > > > foreach(auto __range = range; !__range.empty; > > __range.popFront()) > > { > > > > auto e = __range.front; > > > > } > > > > The compiler knows just enough about ranges to enable foreach, > > but beyond > > that, ranges are entirely a library construct. > > The spec' says "If the foreach range properties do not exist, the > opApply method will be used instead.", does this mean that range > properties take precedence over opApply ? This post gives the current precedence, but there was some discussion of adjusting it a bit: http://forum.dlang.org/post/mailman.275.1342019430.31962.digitalmars- d...@puremagic.com >From the looks of it, opApply gets precedence right now. But mixing ranges and opApply isn't a good idea in most cases, so it doesn't come up very often. - Jonathan M Davis
Re: foreach for ranges?
On Tuesday, 17 July 2012 at 19:27:54 UTC, Jonathan M Davis wrote: It translates foreach(e; range) {} into something like foreach(auto __range = range; !__range.empty; __range.popFront()) { auto e = __range.front; } The compiler knows just enough about ranges to enable foreach, but beyond that, ranges are entirely a library construct. The spec' says "If the foreach range properties do not exist, the opApply method will be used instead.", does this mean that range properties take precedence over opApply ?
Re: foreach for ranges?
On Tuesday, July 17, 2012 20:59:12 Mike L. wrote: > How exactly does the compiler know how to do foreach on ranges > (for example, ones returned by std.algorithm.map ?) I've looked > around in std.algorithm and std.range, but can't seem to figure > it out. It translates foreach(e; range) {} into something like foreach(auto __range = range; !__range.empty; __range.popFront()) { auto e = __range.front; } The compiler knows just enough about ranges to enable foreach, but beyond that, ranges are entirely a library construct. - Jonathan M Davis
Re: foreach for ranges?
On 07/17/2012 11:59 AM, Mike L. wrote: How exactly does the compiler know how to do foreach on ranges (for example, ones returned by std.algorithm.map ?) I've looked around in std.algorithm and std.range, but can't seem to figure it out. The spec mentions it under 'Foreach over Structs and Classes with Ranges': http://dlang.org/statement.html#ForeachStatement There are two methods: a) opApply() member functions (and opApplyReverse(), which is rumored to be deprecated in the future) b) empty, front, and popFront() member functions Ali -- D Programming Language Tutorial: http://ddili.org/ders/d.en/index.html
foreach for ranges?
How exactly does the compiler know how to do foreach on ranges (for example, ones returned by std.algorithm.map ?) I've looked around in std.algorithm and std.range, but can't seem to figure it out.
Re: higher-order funcs for ranges (with usual interface)
On 02/07/2011 01:07 PM, Torarin wrote: If you want to use an interface as a concept, you can take kenji's adaptTo module and add this: template conformsTo(T, Interfaces...) { enum conformsTo = AdaptTo!Interfaces.hasRequiredMethods!T; } and use it like this void draw(T)(T shape) if (conformsTo!(T, Shape, Drawable)) This will of course only work for methods, not properties or aliases, so you still need constraints in some cases. That's nice, thank you. Denis -- _ vita es estrany spir.wikidot.com
Re: higher-order funcs for ranges (with usual interface)
If you want to use an interface as a concept, you can take kenji's adaptTo module and add this: template conformsTo(T, Interfaces...) { enum conformsTo = AdaptTo!Interfaces.hasRequiredMethods!T; } and use it like this void draw(T)(T shape) if (conformsTo!(T, Shape, Drawable)) This will of course only work for methods, not properties or aliases, so you still need constraints in some cases. Torarin
Re: higher-order funcs for ranges (with usual interface)
On 02/07/2011 09:18 AM, Lars T. Kyllingstad wrote: I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily >>> get rid of it in favor of type-classes (built eg as an extension to >>> current interfaces). For instance, instead of: >>> >>> void func (T) (T t) >>> if (is(someConstraint1)&& is(someConstraint2)) >>> { >>> ... >>> } >>> >>> use: >>> >>> void func (SomeTypeClass T) (T t) >>> { >>> ... >>> } >>> >>> For instance (untested): >>> >>> void func (T) (T t) >>> if (isInputRange(T)&& is(ElementType!T == E)) >>> --> >>> void func (InputRange!E T) (T t) >>> >>> where InputRange is a (templated) interface / type-class. >>> >>> Type-class checks on/type/ /template/ parameters (as opposed to type >>> checks on regular value parameters) would be performed structurally >>> (as opposed to nominally). D knows how to do this, since that's what >>> it needs to perform when checking is() constraints. >> >> I agree that is() is rather ugly. Same with __traits. If you haven't >> already done so, I suggest you vote up this issue: >> >> http://d.puremagic.com/issues/show_bug.cgi?id=3702 > > Done! > (I did not get all the details 'cause no time for a deep look, but > anything impulsed by the motivation of getting rid of is() and __traits > can hardly be a Bad Thing ;-) > > What do you think of type classes, as an alternative to Don's proposal > in issue #3702. > See also "Type Classes as Objects and Implicits": > http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf > >> Anyway, you can hide is()'s ugliness in the most common cases, though, >> by defining new templates. For instance, I wouldn't mind having the >> following in std.range as an overload of isInputRange: >> >> template isInputRange(R, T) >> { >> enum isInputRange = isInputRange!R&& is(ElementType!R == T); >> } >> >> Then, you'd simply write >> >> void func(R)(R range) if (isInputRange!(R, E)) { ... } >> >> -Lars > > A great improvement, indeed. > > While we're at defining a set of constraints in a template, let us make > it an interface / type-class that the E must (structurally) satisfy, and > just write: >void func(InputRange!E R)(R range) { ... } > > What do you think? > > Note: a template is not always required, I guess: >void writeElements (Iterable Elements) (Elements elements) { >foreach (element, elements) { >write(element,' '); >} >} > (In this case, because write is itself generic.) How would you deal with the case where the input must satisfy more than one concept/constraint? I mean, for the simple case where you say "R must be an input range of E", sure, type classes/concepts are cleaner. But what about the case where, say, you want R to be an infinite random access range that supports slicing? With template constraints it's simple: void doStuff(R)(R someRange) if (isRandomAccessRange!R&& isInfinite!R&& hasSlicing!R) { ... } Now, I'm no expert on concepts at all---my main sources of information about them are superficial comments on the D newsgroup and a quick browse of the Wikipedia page---but it seems to me that you'd have to define a new concept for each such combination of constraints. Or? Well, dunno really. If a language implements type classes, let us see how things work there :-) Note that above, you use 3 implicite type-class defining check funcs. Agreed? Certainly, because they are common, one wrote a fucn to wrap a bigger set of is() stuff. Replacing them with a type-class interface allows (1) reusing an interface for what it's meant: defining a (super) type, instead of a func which is appropriate and does /not/ correctly convey the meaning (2) avoiding is() Now, you may be right in that type-class check may need be written externally: void doStuff(R)(R someRange) if (RandomAccessRange R && InfiniteRange R && Slicable R) or not: interface ASpecialOne : RandomAccessRange, InfiniteRange, Slicable {} void doStuff (ASpecialOneR) (R someRange) Ain't that clean? For sure, if I was to define a new static PL, I would go /that/ way for generic constraints. This remembers me about another option maybe: when I have time, I'll go and see how XL does it. If (you do not know XL, then /really/ have a look when you have time: http://en.wikipedia.org/wiki/XL_%28programming_language%29 ;esp explore the notion of "conceptual programming") Denis -- _ vita es estrany spir.wikidot.com
Re: higher-order funcs for ranges (with usual interface)
On Thu, 03 Feb 2011 19:11:04 +0100, spir wrote: > On 02/03/2011 02:25 PM, Lars T. Kyllingstad wrote: >> On Thu, 03 Feb 2011 13:53:44 +0100, spir wrote: >> >>> On 02/03/2011 01:17 PM, Lars T. Kyllingstad wrote: Why the reluctance to use template constraints? They're so flexible! :) >>> >>> I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily >>> get rid of it in favor of type-classes (built eg as an extension to >>> current interfaces). For instance, instead of: >>> >>> void func (T) (T t) >>> if (is(someConstraint1)&& is(someConstraint2)) >>> { >>> ... >>> } >>> >>> use: >>> >>> void func (SomeTypeClass T) (T t) >>> { >>> ... >>> } >>> >>> For instance (untested): >>> >>> void func (T) (T t) >>> if (isInputRange(T)&& is(ElementType!T == E)) >>> --> >>> void func (InputRange!E T) (T t) >>> >>> where InputRange is a (templated) interface / type-class. >>> >>> Type-class checks on /type/ /template/ parameters (as opposed to type >>> checks on regular value parameters) would be performed structurally >>> (as opposed to nominally). D knows how to do this, since that's what >>> it needs to perform when checking is() constraints. >> >> I agree that is() is rather ugly. Same with __traits. If you haven't >> already done so, I suggest you vote up this issue: >> >>http://d.puremagic.com/issues/show_bug.cgi?id=3702 > > Done! > (I did not get all the details 'cause no time for a deep look, but > anything impulsed by the motivation of getting rid of is() and __traits > can hardly be a Bad Thing ;-) > > What do you think of type classes, as an alternative to Don's proposal > in issue #3702. > See also "Type Classes as Objects and Implicits": > http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf > >> Anyway, you can hide is()'s ugliness in the most common cases, though, >> by defining new templates. For instance, I wouldn't mind having the >> following in std.range as an overload of isInputRange: >> >>template isInputRange(R, T) >>{ >>enum isInputRange = isInputRange!R&& is(ElementType!R == T); >>} >> >> Then, you'd simply write >> >>void func(R)(R range) if (isInputRange!(R, E)) { ... } >> >> -Lars > > A great improvement, indeed. > > While we're at defining a set of constraints in a template, let us make > it an interface / type-class that the E must (structurally) satisfy, and > just write: > void func(InputRange!E R)(R range) { ... } > > What do you think? > > Note: a template is not always required, I guess: > void writeElements (Iterable Elements) (Elements elements) { > foreach (element, elements) { > write(element,' '); > } > } > (In this case, because write is itself generic.) How would you deal with the case where the input must satisfy more than one concept/constraint? I mean, for the simple case where you say "R must be an input range of E", sure, type classes/concepts are cleaner. But what about the case where, say, you want R to be an infinite random access range that supports slicing? With template constraints it's simple: void doStuff(R)(R someRange) if (isRandomAccessRange!R && isInfinite!R && hasSlicing!R) { ... } Now, I'm no expert on concepts at all---my main sources of information about them are superficial comments on the D newsgroup and a quick browse of the Wikipedia page---but it seems to me that you'd have to define a new concept for each such combination of constraints. Or? -Lars
Re: higher-order funcs for ranges (with usual interface)
On 02/03/2011 02:25 PM, Lars T. Kyllingstad wrote: On Thu, 03 Feb 2011 13:53:44 +0100, spir wrote: On 02/03/2011 01:17 PM, Lars T. Kyllingstad wrote: Why the reluctance to use template constraints? They're so flexible! :) I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily get rid of it in favor of type-classes (built eg as an extension to current interfaces). For instance, instead of: void func (T) (T t) if (is(someConstraint1)&& is(someConstraint2)) { ... } use: void func (SomeTypeClass T) (T t) { ... } For instance (untested): void func (T) (T t) if (isInputRange(T)&& is(ElementType!T == E)) --> void func (InputRange!E T) (T t) where InputRange is a (templated) interface / type-class. Type-class checks on /type/ /template/ parameters (as opposed to type checks on regular value parameters) would be performed structurally (as opposed to nominally). D knows how to do this, since that's what it needs to perform when checking is() constraints. I agree that is() is rather ugly. Same with __traits. If you haven't already done so, I suggest you vote up this issue: http://d.puremagic.com/issues/show_bug.cgi?id=3702 Done! (I did not get all the details 'cause no time for a deep look, but anything impulsed by the motivation of getting rid of is() and __traits can hardly be a Bad Thing ;-) What do you think of type classes, as an alternative to Don's proposal in issue #3702. See also "Type Classes as Objects and Implicits": http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf Anyway, you can hide is()'s ugliness in the most common cases, though, by defining new templates. For instance, I wouldn't mind having the following in std.range as an overload of isInputRange: template isInputRange(R, T) { enum isInputRange = isInputRange!R&& is(ElementType!R == T); } Then, you'd simply write void func(R)(R range) if (isInputRange!(R, E)) { ... } -Lars A great improvement, indeed. While we're at defining a set of constraints in a template, let us make it an interface / type-class that the E must (structurally) satisfy, and just write: void func(InputRange!E R)(R range) { ... } What do you think? Note: a template is not always required, I guess: void writeElements (Iterable Elements) (Elements elements) { foreach (element, elements) { write(element,' '); } } (In this case, because write is itself generic.) Denis -- _ vita es estrany spir.wikidot.com
Re: higher-order funcs for ranges (with usual interface)
On Thu, 03 Feb 2011 13:53:44 +0100, spir wrote: > On 02/03/2011 01:17 PM, Lars T. Kyllingstad wrote: >> Why the reluctance to use template constraints? They're so flexible! >> :) > > I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily get > rid of it in favor of type-classes (built eg as an extension to current > interfaces). For instance, instead of: > > void func (T) (T t) > if (is(someConstraint1) && is(someConstraint2)) > { > ... > } > > use: > > void func (SomeTypeClass T) (T t) > { > ... > } > > For instance (untested): > > void func (T) (T t) > if (isInputRange(T) && is(ElementType!T == E)) > --> > void func (InputRange!E T) (T t) > > where InputRange is a (templated) interface / type-class. > > Type-class checks on /type/ /template/ parameters (as opposed to type > checks on regular value parameters) would be performed structurally (as > opposed to nominally). D knows how to do this, since that's what it > needs to perform when checking is() constraints. I agree that is() is rather ugly. Same with __traits. If you haven't already done so, I suggest you vote up this issue: http://d.puremagic.com/issues/show_bug.cgi?id=3702 Anyway, you can hide is()'s ugliness in the most common cases, though, by defining new templates. For instance, I wouldn't mind having the following in std.range as an overload of isInputRange: template isInputRange(R, T) { enum isInputRange = isInputRange!R && is(ElementType!R == T); } Then, you'd simply write void func(R)(R range) if (isInputRange!(R, E)) { ... } -Lars
Re: higher-order funcs for ranges (with usual interface)
On 02/03/2011 01:17 PM, Lars T. Kyllingstad wrote: Why the reluctance to use template constraints? They're so flexible! :) I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily get rid of it in favor of type-classes (built eg as an extension to current interfaces). For instance, instead of: void func (T) (T t) if (is(someConstraint1) && is(someConstraint2)) { ... } use: void func (SomeTypeClass T) (T t) { ... } For instance (untested): void func (T) (T t) if (isInputRange(T) && is(ElementType!T == E)) --> void func (InputRange!E T) (T t) where InputRange is a (templated) interface / type-class. Type-class checks on /type/ /template/ parameters (as opposed to type checks on regular value parameters) would be performed structurally (as opposed to nominally). D knows how to do this, since that's what it needs to perform when checking is() constraints. Denis -- _ vita es estrany spir.wikidot.com
Re: higher-order funcs for ranges (with usual interface)
On Thu, 03 Feb 2011 13:05:00 +0100, spir wrote: > On 02/03/2011 08:41 AM, Lars T. Kyllingstad wrote: >> On Wed, 02 Feb 2011 18:38:02 +0100, spir wrote: > >>> 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 > > Yes, I know about concepts ;-) (and typestates, and such). That's not > what I mean but I could not find how to express it. What I have in mind > is a way to simply express just like is > expressed by "T[]". But indeed the issue is there is only one type of > array of T, while there are an infinity of types of ranges of T. > Your solution below is a good alternative. > >> 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. > > Right, this is more or less what I was looking for. And I think I can > restrict cases to ranges beeing of the same "kind". If necessary, the > result can then be mapped onto another kind of range (hopefully lazily). > The only "un-workaround-able" situation is, I guess, when the source > range is infinite ;-) Why the reluctance to use template constraints? They're so flexible! :) -Lars
Re: higher-order funcs for ranges (with usual interface)
On 02/03/2011 08:41 AM, Lars T. Kyllingstad wrote: On Wed, 02 Feb 2011 18:38:02 +0100, spir wrote: 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 Yes, I know about concepts ;-) (and typestates, and such). That's not what I mean but I could not find how to express it. What I have in mind is a way to simply express just like is expressed by "T[]". But indeed the issue is there is only one type of array of T, while there are an infinity of types of ranges of T. Your solution below is a good alternative. 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. Right, this is more or less what I was looking for. And I think I can restrict cases to ranges beeing of the same "kind". If necessary, the result can then be mapped onto another kind of range (hopefully lazily). The only "un-workaround-able" situation is, I guess, when the source range is infinite ;-) Denis -- _ vita es estrany spir.wikidot.com
Re: higher-order funcs for ranges (with usual interface)
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: , > syntactically and in the param set, just like > 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
Re: higher-order funcs for ranges (with usual interface)
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: , syntactically and in the param set, just like 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
Re: higher-order funcs for ranges (with usual interface)
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. 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)) { ... } 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(); } } 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); } -Lars
Re: higher-order funcs for ranges (with usual interface)
On Wed, 02 Feb 2011 13:18:07 +, Lars T. Kyllingstad wrote: > [...] > > 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(); } > } > > 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); > } Oops, seems i missed a few closing parentheses on the template constraints. -Lars
higher-order funcs for ranges (with usual interface)
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? I mean, with the same kind of interface to client code (not the interface of std.algo.map). And is there a way to write it so that it works both for ranges and other kinds of sequences (arrays, strings); or even for anything "iterable" (AA, set, list, tree...). 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[]. Denis -- _ vita es estrany spir.wikidot.com