On 4/30/20 6:39 PM, Paul Backus wrote:
On Thursday, 30 April 2020 at 18:30:14 UTC, H. S. Teoh wrote:
Also, for ranges based on generator functions, if .front is lazy then
you need to keep extra baggage around your range to indicate whether
or not the generator has been invoked yet; it's easier to just always
compute the next element eagerly and cache it, and .front just returns
the cached data.
std.range.generate is actually a perfect example of the problem with
doing work in popFront. Because it has to call popFront both on
construction *and* after every element, consuming n elements will call
the generator function n+1 times. The final call is completely wasted.
generate used to do everything on front. But that meant it wasn't a true
range as multiple calls to front generated different data (popFront was
a noop). I fixed it a while ago.
It should be the status quo to do all work in popFront, and then you
should wrap if you need different behavior.
I'm ok with something like this (I'm kind of surprised something like
this doesn't exist already):
struct LazyRange(R)
{
R src;
bool frontEvaluated;
void sync() {
if(!frontEvaluated) {
src.popFront;
frontEvaluated = true;
}
}
auto front() {
sync();
return src.front;
}
bool empty() {
sync();
return src.empty;
}
void popFront() {
sync();
frontEvaluated = false;
}
}
-Steve