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

Reply via email to