On 10.12.2010 3:26, Andrei Alexandrescu wrote:
Jonathan M. Davis has diligently worked on his std.datetime proposal, and it has been through a few review cycles in this newsgroup.

It's time to vote. Please vote for or against inclusion of datetime into Phobos, along with your reasons.


Thank you,

Andrei

First, let me state my concerns.
I'm very disappointed with how the range interface introduced in std.datetime, let me pick an example from ddoc:
auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27));
auto func = IRange.everyDayOfWeek!(Date, >>>Direction.bwd <<<)(DayOfWeek.fri);
auto range = interval.>>>bwdRange<<<(func);

*(emphasis mine)
Note the verbosity caused by conflating two concepts - the flexible way of getting a range of time points by consequently applying the delegate to a given start time point, and traversing an interval of time in a certain direction.

Time ago I pointlessly argued with Jonathon to just drop the flexible way of iterating intervals, replacing it by simplistic "stride by a adding given duration to the begining of interval until you hit end". In the end I observed it was very limited view as it can't effectively solve the "give me the next five Easters" problem, and hastily retreated :)

So I to come up with an alternative, see the attached sketch of it. The main idea - provide and infinite range based on aforementioned flexible principle, simple example is (for ints):
auto r = powersOf((int x){ return x*2; }, 1); // 2,4,8,...
given the fact that delegate already contains information about direction of traversing (if any), the user the free to use it as is:
take(r,10); // 2,4... 1024

in case with dates that is:
auto inf = powersOf(IRange.everyDayOfWeek!(Date)(DayOfWeek.monday), Date(2010,Month.dec,11));
take(inf,5);//next 5 mondays, forward is default in everyDayOfWeek

Then comes the second concept of confining ranges in intervals, here it goes: auto interval = Interval!Date(Date(2010,Month.dec,1),Date(2010,Month.dec,20)); auto conf = confine(inf,interval);//Mondays in the interval iterated from the beginning

And the simplistic approach of fixed duration striding:
fwdRange(interval, dur!"days"(2)); //yup, traverse by two days
same goes for bwdRange

To summarize it:
1) remove fwdRange, bwdRange from each of Interval types (all it does is to require to retype them all over again) and make them free functions with above simple functionality 2) drop IRange, and let those delegates not handled by simple case to just float around (they are very unlikely to conflict with anything IMHO) 3) speaking of which - separate the notion of applying delegate (that could even be generalized in std.algorithm someday) to obtain range, and confinement of produced range to interval.

A fitting finale would be to say... that _yes_, I would like to see this library in Phobos!
Though with just proposed change if gets any traction...
Overall it's a fantastic work, thanks Jonathon.

--

Dmitry Olshansky

import datetime;
import std.range,std.algorithm,std.stdio,std.traits;

///Infinite range producing natural powers of functor f using given seed s :
/// f(s), f(f(s)), f(f(f(s))), ...
struct PowerRange(Fn,T) if(isCallable!Fn){
    Fn _fn;
    T _val;
   
    this(Fn fn,T val){
       _val = val;
       _fn = fn;
        // could be parametrized to start with different index 
       popFront();       
    }
    void popFront(){
        _val = _fn(_val);
    }
    bool empty()const nothrow{
        return false;
    }
    typeof(this) save(){ return this; }
    T front(){
        return _val;
    }
}

PowerRange!(Fn,TP) powersOf(Fn,TP)(Fn fn,TP start){
    return PowerRange!(Fn,TP)(fn,start);
}


unittest{
    auto r = powersOf((int x){ return x*2; }, 1);
    assert(array(take(r,10)) == [2,4,8,16,32,64,128,256,512,1024]);
    static assert(isForwardRange!(typeof(r)));
}

///Range that confines given infinite forward timepoint range
///the result is forward range containing only inters
struct ConfinedForwardRange(Range,I) if( isForwardRange!Range ){
    private Range _range;
    private I _interval;
    static if(__traits(compiles,_interval.begin())){
        alias typeof(_interval.begin()) TP;
    }else static if (is(_interval.end)){
        alias typeof(_interval.end()) TP;
    }else
        static assert(0);
    private bool _empty;
    this(Range range, I interval){
        _range = range;
        _interval = interval;
        _empty = !_interval.contains(_range.front());
    }
    TP front(){
        assert(!empty);
        return _range.front();
    }
    typeof(this) save(){ return this; }
    bool empty() { return _empty; }
    void popFront(){
        _range.popFront();
        _empty = !_interval.contains(_range.front());
    }
}

ConfinedForwardRange!(Range,I) confine(Range,I)(Range range,I interval) {
    return ConfinedForwardRange!(Range,I)(range,interval);
}
///Infinite forward range of timepoints obtained 
///by continiously applying add!dur to a given timepoint
struct SimpleRange(TP,Direction dir=Direction.Fwd) if(isTimePoint!(TP)){ 
    TP _cur;
    Duration _step;
    static if(__traits(compiles,_cur.year)){
        int _ystep, _mstep;
        this(TP start, int years, int months=0,Duration step=dur!"days"(0)){
            _cur = start;
            _step = step;
            _ystep = years;
            _mstep = months;
        }
    }
    this(TP start, Duration step){
        _cur = start;
        _step = step;
    }
    void popFront(){ 
        static if(dir == Direction.fwd){
                static if(__traits(compiles,_cur.year)){
                        _cur.add!"years"(_ystep);
                        _cur.add!"months"(_mstep);
                }
                 _cur += _step; 
        }else{
                _cur -= _step;
                static if(__traits(compiles,_cur.year)){
                        _cur.add!"months"(-_mstep);
                        _cur.add!"years"(-_ystep);
                }
        }
    }
    bool empty(){ return false; }
    TP front(){ return _cur; } 
    typeof(this) save(){ return this; }   
}

/// Helper function, constructs forward range of timepoints of a given 
interval, striding by adding duration d 
ConfinedForwardRange!(SimpleRange!(typeof(I.begin),Direction.fwd),I) 
fwdRange(I)(I interval, Duration d){
    alias typeof(interval.begin) TP;
    return confine(SimpleRange!(TP,Direction.fwd)(interval.begin,d), interval);
}

/// Helper function, constructs forward range of timepoints of a given 
interval, striding by adding duration d, then months, then years
ConfinedForwardRange!(SimpleRange!(typeof(I.begin),Direction.fwd),I) 
fwdRange(I)(I interval, int years, int months=0,Duration d=dur!"days"(0)){
    alias typeof(interval.begin) TP;
    return 
confine(SimpleRange!(TP,Direction.fwd)(interval.begin,years,months,d), 
interval);
}

/// Helper function, constructs forward range of timepoints of a given 
interval, striding backwards by subtracting duration d
ConfinedForwardRange!(SimpleRange!(typeof(I.end),Direction.bwd),I) 
bwdRange(I)(I interval, Duration d){
    alias typeof(interval.end) TP;
    auto r =  confine(SimpleRange!(TP,Direction.bwd)(interval.end,d), interval);
    r.popFront(); //interval is open ended, which means the last time point is 
NOT in the interval 
    return r;
}

/// Helper function, constructs forward range of timepoints of a given 
interval, striding backwards by subtracting duration d, then months, then years
ConfinedForwardRange!(SimpleRange!(typeof(I.end),Direction.bwd),I) 
bwdRange(I)(I interval, int years, int months=0, Duration d=dur!"days"(0)){
    alias typeof(interval.end) TP;
    auto r =  
confine(SimpleRange!(TP,Direction.bwd)(interval.end,years,months,d), interval);
    r.popFront(); //interval is open ended, which means the last time point is 
NOT in the interval 
    return r;
}

//that should be packed in a unittest
void main(){ 
    auto evd = IRange.everyDayOfWeek!(Date)(DayOfWeek.monday);
    auto inf = powersOf(evd, Date(2010,Month.dec,11));
    writeln(take(inf,5));//next 5 mondays

    auto interval = 
Interval!Date(Date(2010,Month.dec,1),Date(2010,Month.dec,20));
    auto conf = confine(inf,interval);
    writeln(conf);//mondays in given interval

    writeln(fwdRange(interval, dur!"days"(2)));  //2010-Dec-01, 2010-Dec-03...
    writeln(bwdRange(interval, dur!"days"(3)));  //interval is open ended, so: 
2010-Dec-17, 2010-Dec-14, 2010-Dec-11...

    auto interval2 = 
Interval!Date(Date(2010,Month.mar,3),Date(2022,Month.apr,2));

    writeln(fwdRange(interval2, 1, 2)); //2010-Mar-03, 2011-May-03, 
2012-Jul-03, 2013-Sep-03, 2014-Nov-03, 2016-Jan-03..

    writeln(bwdRange(interval2, 2, 1)); //interval is open ended, so: 
2020-Mar-02, 2018-Feb-02, 2016-Jan-02, 2013-Dec-02, 2011-Nov-02 ...
}

Reply via email to