Alternative to std.range.choose
WebFreak001
d.forum at webfreak.org
Wed Jul 22 06:16:44 UTC 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);
}
More information about the Digitalmars-d-learn
mailing list