digitalmars.D - std.range.cacheFront proposal&working code: wraps a range to enforce
- Timothee Cour (89/89) Oct 22 2013 Following the recent thread "front evaluated multiple time with joiner
- deadalnix (5/5) Oct 23 2013 Overall, I think it is greatly needed :D
- Timothee Cour (6/11) Oct 24 2013 some range properties still make sense to forward:
Following the recent thread "front evaluated multiple time with joiner depending on where extra arg given", I'd like to propose the following addition in std.range : The goal is to ensure that a given range's 'front' method is called only once per element, allowing one to handle safely side effects in 'front' methods eg use case: lambdas with side effects given to a map/reduce/filter function, etc. import std.traits; import std.range; struct CacheFront(R){ alias T=ElementType!R; R a; import util.traits; enum isRef=mixin(isLvalue(q{a.front})); static if(isRef) T* ai; else{ T ai; bool isValid=false; } auto ref front(){ //TODO: could also depend on whether front is pure static if(isRef){ if(ai) return *ai; else{ ai=&a.front; return *ai; } } else{ if(isValid){ return ai; } else{ isValid=true; ai=a.front; return ai; } } } void popFront(){ a.popFront; static if(isRef) ai=null; else isValid=false; } //forward other properties automatically: alias a this; } auto cacheFront(R)(R a) if(isInputRange!R){ return CacheFront!R(a); } //helper function (should be in phobos' std.traits) void requireLvalue(T)(ref T); string isLvalue(string a){ return `__traits(compiles, requireLvalue(`~a~`))`; } void main(){ import std.algorithm; import std.array; { //checks that it calls front only once per element uint counter; auto b=[1,2,3].map!((a){counter++; return [a];}).cacheFront.joiner.array; assert(counter==3); assert(b==[1,2,3]); } { int counter=0; auto b=[1,2,3].map!((a){counter++; return [a];}).joiner.array; assert(counter==6); } { //checks that it works with ref front auto a0=[1,2,3]; auto ref fun0(ref int a){a=0; return a;} auto b=a0. cacheFront.map!fun0.array; assert(b==[0,0,0]); assert(a0==[0,0,0]); //checks that it forwards properties of range: assert([1,2,3]. cacheFront.length==3); } } And a side question: is there a way to denote that a lambda can return by ref?
Oct 22 2013
Overall, I think it is greatly needed :D You should propose that as a pull request. Also, you shouldn't alias this the source range, as thing now become impracticable. IMO, CacheFront should be a froward range whatever the source is and only provide front/popFront.
Oct 23 2013
On Wed, Oct 23, 2013 at 5:47 PM, deadalnix <deadalnix gmail.com> wrote:Overall, I think it is greatly needed :D You should propose that as a pull request. Also, you shouldn't alias this the source range, as thing now become impracticable.could you elaborate on that?IMO, CacheFront should be a froward range whatever the source is and only provide front/popFront.some range properties still make sense to forward: * length if it's there * if the range is random access, I could use memoize (on array index) * other cases possible
Oct 24 2013