In general, who should do more work: popFront or front?

jfondren julian.fondren at gmail.com
Tue Jun 15 04:43:38 UTC 2021


On Tuesday, 15 June 2021 at 04:24:09 UTC, surlymoor wrote:
> All my custom range types perform all their meaningful work in 
> their respective popFront methods, in addition to its expected 
> source data iteration duties. The reason I do this is because I 
> swear I read in a github discussion that front is expected to 
> be O(1), and the only way I can think to achieve this is to 
> stash the front element of a range in a private field; popFront 
> would thus also set this field to a new value upon every call, 
> and front would forward to it. (Or front would be the cache 
> itself.)
> At the moment, I feel that as long as the stashed front element 
> isn't too "big" (For some definition of big, I guess.), that 
> built-in caching should be fine. But is this acceptable? What's 
> the best practice for determining which range member should 
> perform what work? (Other than iterating, of course.)

Well, consider this program:

```d
import std;

struct Noisy {
     int[] source;
     int pops, fronts;
     bool empty() { return source.empty; }
     void popFront() { writeln("popFront #", ++pops); 
source.popFront; }
     int front() { writeln("front #", ++fronts); return 
source.front; }
}

void main() {
     iota(5).array
         .Noisy
         .filter!"a%2"
         .each!writeln;
}
```

Out of [0,1,2,3,4], only 1,3 pass the filter.
Noisy's front is called seven times, 2x for each filter success.
Noisy's popFront is called five times, 1x for each source member.

But if you slap a .cache (from std.algorithm.cache) before the
.filter then these counts are the same.


More information about the Digitalmars-d-learn mailing list