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 ...
}