digitalmars.D.learn - Alternative to std.range.choose
- James Gray (26/26) Jul 21 2020 Is there a better way to achieve behaviour similar to rangeFuncIf
- WebFreak001 (41/68) Jul 21 2020 it seems `choose` evaluates both arguments instead of using lazy
- James Gray (2/10) Jul 22 2020 Thank you very much.
- Steven Schveighoffer (9/38) Jul 22 2020 I know this is a contrived example, but choose is not good for such a
Is there a better way to achieve behaviour similar to rangeFuncIf below? f gives a contrived example of when one might want this. g is how one might try and achieve the same with std.range.choose. import std.stdio; import std.range : only, chain, join, choose; import std.algorithm : map; auto rangeFunctIf(alias F1, alias F2)(bool c) if ( __traits(compiles,F1().chain(F2()))) { return only(true).repeat(c?1:0).map!(x=>F1()).join .chain(only(true).repeat(c?0:1).map!(x=>F2()).join); } auto f(ulong n) { return (n!=0uL).rangeFuncIf!(()=>only(100/n), ()=>only(0)); } auto g(ulong n) { return choose(n!=0uL,only(100/n),only(0)); } void main() { writeln(f(2)); writeln(f(0)); writeln(g(2)); //writeln(g(0)); <---- runtime error }
Jul 21 2020
On Wednesday, 22 July 2020 at 04:33:20 UTC, James Gray wrote:Is there a better way to achieve behaviour similar to rangeFuncIf below? f gives a contrived example of when one might want this. g is how one might try and achieve the same with std.range.choose. import std.stdio; import std.range : only, chain, join, choose; import std.algorithm : map; auto rangeFunctIf(alias F1, alias F2)(bool c) if ( __traits(compiles,F1().chain(F2()))) { return only(true).repeat(c?1:0).map!(x=>F1()).join .chain(only(true).repeat(c?0:1).map!(x=>F2()).join); } auto f(ulong n) { return (n!=0uL).rangeFuncIf!(()=>only(100/n), ()=>only(0)); } auto g(ulong n) { return choose(n!=0uL,only(100/n),only(0)); } void main() { writeln(f(2)); writeln(f(0)); writeln(g(2)); //writeln(g(0)); <---- runtime error }it seems `choose` evaluates both arguments instead of using lazy evaluation. IMO this is a broken API to me but it has been like this for longer so this would be difficult to change. Additionally with regards to storing in memory this is another problem. However I think using .init this is solvable, so here is an alternative choose function which you can just use as drop-in replacement: import std.traits : Unqual; auto choose(R1, R2)(bool condition, return scope lazy R1 r1, return scope lazy R2 r2) if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && !is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void)) { alias ChooseResult = __traits(getMember, std.range, "ChooseResult"); return ChooseResult!(R1, R2)(condition, condition ? r1 : R1.init, condition ? R2.init : r2); } The parameters are lazy so they are only evaluated once accessed, the ChooseResult can only store non-lazy parameters so we pass in dummy (.init) parameters there which are hopefully never used. The `alias ChooseResult = __traits(getMember, std.range, "ChooseResult");` is needed because ChooseResult is private, but we want to access it anyway. A little bit more instantiation heavy but effectively the same effect, though a little more stable API usage would be: auto choose(R1, R2)(bool condition, return scope lazy R1 r1, return scope lazy R2 r2) if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && !is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void)) { import std.range : choose; return choose(condition, condition ? r1 : R1.init, condition ? R2.init : r2); }
Jul 21 2020
On Wednesday, 22 July 2020 at 06:16:44 UTC, WebFreak001 wrote:On Wednesday, 22 July 2020 at 04:33:20 UTC, James Gray wrote:Thank you very much.[...]it seems `choose` evaluates both arguments instead of using lazy evaluation. IMO this is a broken API to me but it has been like this for longer so this would be difficult to change. Additionally with regards to storing in memory this is another problem. [...]
Jul 22 2020
On 7/22/20 12:33 AM, James Gray wrote:Is there a better way to achieve behaviour similar to rangeFuncIf below? f gives a contrived example of when one might want this. g is how one might try and achieve the same with std.range.choose. import std.stdio; import std.range : only, chain, join, choose; import std.algorithm : map; auto rangeFunctIf(alias F1, alias F2)(bool c) if ( __traits(compiles,F1().chain(F2()))) { return only(true).repeat(c?1:0).map!(x=>F1()).join .chain(only(true).repeat(c?0:1).map!(x=>F2()).join); } auto f(ulong n) { return (n!=0uL).rangeFuncIf!(()=>only(100/n), ()=>only(0)); } auto g(ulong n) { return choose(n!=0uL,only(100/n),only(0)); } void main() { writeln(f(2)); writeln(f(0)); writeln(g(2)); //writeln(g(0)); <---- runtime error }I know this is a contrived example, but choose is not good for such a thing. It should only be used if the range types are different. g could be: auto g(ulong n) { return only(n != 0L ? 100/n : 0) } But in any case, changing choose to use lazy is probably a good answer. -Steve
Jul 22 2020