cache() method - should it cache on popFront() or on first call to front()
Piotr Mitana
piotr.mitana at gmail.com
Mon Feb 10 10:43:33 UTC 2020
I have recently found std.algorithm.iteration.cache() function's
behavior a little bit confusing. I'll demonstrate it with this
example:
void main()
{
auto arr = sequence!"n"
.map!(n => { writefln("Mapped: %d", n); return n; }())
.cache
.until(3, No.openRight)
.array;
arr.writeln;
}
What would you expect such a code to write? Actually what it
writes is:
Mapped: 0
Mapped: 1
Mapped: 2
Mapped: 3
Mapped: 4
[0, 1, 2, 3]
What was unexpected to me is that it still passed 4 to map
function despite the fact that I should be left out by until.
Later I have read that range's element is calculated eagerly when
the popFront() is called.
However, when the until() is called immediately after, there is
element is calculated needlessly. This could be avoided by
caching not when popFront() is called, but when front() is called
for the first time of the new element.
I solved the problem with the following simple template:
auto cacheOnAccess(Range)(Range source) if(isInputRange!Range)
{
struct CacheOnAccess
{
Range source;
Nullable!(ElementType!Range) cachedFront;
this(Range source)
{
this.source = source;
}
@property ElementType!Range front()
{
if(cachedFront.isNull)
cachedFront = source.front.nullable;
return cachedFront.get;
}
@property bool empty()
{
return source.empty;
}
void popFront()
{
source.popFront();
cachedFront.nullify();
}
}
return CacheOnAccess(source);
}
Maybe a change in Phobos could be considered - either adding a
parameter to cache() template, so that the moment of caching
could be determined? Alternatively, another function could be
added, which would cache when the element is accessed for the
first time.
More information about the Digitalmars-d
mailing list