Unless I'm missing something, after several separate discussions on the D IRC, I could not find a way to run a function 'n' times lazily for a range without doing hackery's like this:
iota(100).map!(x => uniform(0, 76));
The use case is where you want to repeat an (impure) function n times lazily.
A possible example:
auto five_random_ints = take(recurrence!(uniform!int), 5)
or
auto five_random_ints = repeat!(uniform!int)(5);
If there is an idiomatic way to do this already that I am missing, sorry for taking up your time.
Comment #1 by monarchdodra — 2013-02-20T00:16:07Z
Isn't what you are asking for a special case of sequence that discards any and all input?
//----
int fun(T)(T/+Tuple!()+/, uint n)
{
static int i;
return ++++i;
}
void main()
{
auto a = sequence!fun();
writeln(a.take(10));
}
//----
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
//----
In this case "T" is a tuple containing the empty state, and n is the iteration count.
Apart from the fancy sig, it's what you are asking for.
Given that the signature of "fun" isn't specified yet, we could special case sequence for sequences that have no initial state, to simply call fun(n) (as well as be more efficient).
I think it would be a good idea. So I'll look into doing that. PS: could you link the mentioned discussion, it could help.
Comment #2 by daniel350 — 2013-02-20T00:31:05Z
(In reply to comment #1)
> Isn't what you are asking for a special case of sequence that discards any and
> all input?
>
> //----
> int fun(T)(T/+Tuple!()+/, uint n)
> {
> static int i;
> return ++++i;
> }
>
> void main()
> {
> auto a = sequence!fun();
> writeln(a.take(10));
> }
> //----
> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
> //----
>
> In this case "T" is a tuple containing the empty state, and n is the iteration
> count.
>
> Apart from the fancy sig, it's what you are asking for.
>
> Given that the signature of "fun" isn't specified yet, we could special case
> sequence for sequences that have no initial state, to simply call fun(n) (as
> well as be more efficient).
>
> I think it would be a good idea. So I'll look into doing that. PS: could you
> link the mentioned discussion, it could help.
Granted, that is what I'm asking for, but it seems as hackish (if not more confusing) then the iota solution.
The sequence solution does not sound very intuitive given the fact the output would change on each retrieval/take from the range.
The discussions have been over a many conversations, as I have run into this problem several times now, and finally resolved it isn't just something I have missed in phobos.
Comment #3 by monarchdodra — 2013-02-20T01:15:30Z
(In reply to comment #2)
> (In reply to comment #1)
> > Isn't what you are asking for a special case of sequence that discards any and
> > all input?
> >
> > //----
> > int fun(T)(T/+Tuple!()+/, uint n)
> > {
> > static int i;
> > return ++++i;
> > }
> >
> > void main()
> > {
> > auto a = sequence!fun();
> > writeln(a.take(10));
> > }
> > //----
> > [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
> > //----
> >
> > In this case "T" is a tuple containing the empty state, and n is the iteration
> > count.
> >
> > Apart from the fancy sig, it's what you are asking for.
> >
> > Given that the signature of "fun" isn't specified yet, we could special case
> > sequence for sequences that have no initial state, to simply call fun(n) (as
> > well as be more efficient).
> >
> > I think it would be a good idea. So I'll look into doing that. PS: could you
> > link the mentioned discussion, it could help.
>
> Granted, that is what I'm asking for, but it seems as hackish (if not more
> confusing) then the iota solution.
>
> The sequence solution does not sound very intuitive given the fact the output
> would change on each retrieval/take from the range.
>
> The discussions have been over a many conversations, as I have run into this
> problem several times now, and finally resolved it isn't just something I have
> missed in phobos.
FYI, sequence caches it's result, so if you just call front twice in a row, you *won't* get different values for each front. map *doesn't* cache its result, so you *will* get different results if you call front twice in a row.
As for intuitiveness, I personally prefer "sequence" over "map", but that may be because I actually use sequence and recurrence, so I'm more used to it.
A third alternative you could use is, instead of iota, you may want to consider using repeat:
//----
int fun(int)
{
static int i;
return ++++i;
}
void main()
{
auto a = repeat(0).map!fun();
writeln(a.take(10));
}
//----
The "advantage" this approach is that you define the range *mechanics* (repeat+map) first, and then build the bounds on top of that (take).
Since take is infinite, there are chances it'll run faster too (operations such as length/empty will be easier to compute, since carried by the "top", as opposed to the "bottom").
//--------
But enough off-topic. You opened an enhancement request. Could you maybe formalize your need a bit more, tell us in more detail what the semantics of the range you are trying to build are? How you would use such a range? This would help us better address the issue.
Comment #4 by dlang-bugzilla — 2017-06-25T17:00:15Z
(In reply to daniel from comment #0)
> The use case is where you want to repeat an (impure) function n times lazily.
This doesn't make sense to me. Laziness and impurity do not mix.
> iota(100).map!(x => uniform(0, 76));
This code is conceptually wrong. By itself, this code does nothing. Advancing the range will do nothing. Advancing the range while evaluating .front will do something; but so will accessing .front in a loop without advancing the range. The resulting range object is broken by design in several aspects.
It seems to me that you are trying to shoehorn an operation into a design which was not really meant for it. In the case of random numbers, the proper solution would be a PRNG range, which has correctly-implemented .front (which always returns the same value while not advancing), .popFront (which advances the PRNG), and .save (which makes a copy of the PRNG's state).
I'll close this as the last activity on this bug was over 4 years ago. If you think you have a valid use case, feel free to reopen, though I would suggest creating a forum thread about this first instead.