std.range.cacheFront proposal&working code: wraps a range to enforce front is called only once

Timothee Cour thelastmammoth at gmail.com
Tue Oct 22 23:40:00 PDT 2013


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?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20131022/759d14c7/attachment.html>


More information about the Digitalmars-d mailing list