H. S. Teoh:
OK, here's a draft of the article:
http://wiki.dlang.org/User:Quickfur/Component_programming_with_ranges
Most of the code below is not tested. So my suggestions may
contain bugs or mistakes.
A bit improved chunkBy could go in Phobos.
---------------
For our purposes, though, we can't just do this in a loop,
because it has to interface with the other components, which do
not have a matching structure to a loop over dates.<
It's just because D doesn't yet have a yield designed like C#.
"yield" for coroutines is a very nice kind of glue.
---------------
auto datesInYear(int year) {
return Date(year, 1, 1)
.recurrence!((a,n) => a[n-1] + dur!"days"(1))
.until!(a => a.year > year);
}
===>
auto datesInYear(in uint year) pure /*nothrow*/
in {
assert(year > 1900);
} body {
return Date(year, 1, 1)
.recurrence!((a, n) => a[n - 1] + dur!"days"(1))
.until!(d => d.year > year);
}
I suggest to align the dots vertically like that. And generally
_all_ variables/arguments that don't need to mutate should be
const or immutable (or enum), unless holes in Phobos or in the
type system or other factors prevent you to do it.
---------------
return chunkBy!"a.month()"(dates);
===>
return dates.chunkBy!q{ a.month };
---------------
byWeek() is not so simple. Most of its code is boilerplate code.
Perhaps using a "yield" it becomes simpler.
---------------
string spaces(size_t n) {
return repeat(' ').take(n).array.to!string;
}
===>
string spaces(in size_t n) pure nothrow {
return std.array.replicate(" ", n);
}
Currently in programs that import both std.range and std.array
you have to qualify the module for replicate.
In Python this is just:
' ' * n
---------------
auto buf = appender!string();
Perhaps this suffices:
appender!string buf;
---------------
string[] days = map!((Date d) => " %2d".format(d.day))(r.front)
.array;
(not tested) ==>
const days = r.front.map!(d => " %2d".format(d.day)).array;
Or:
const string[] days = r
.front
.map!(d => " %2d".format(d.day))
.array;
---------------
If you put the days inside buf, do you really need to turn days
into an array with array()?
string[] days = map!((Date d) => " %2d".format(d.day))(r.front)
.array;
assert(days.length <= 7 - startDay);
days.copy(buf);
Isn't this enough?
auto days = r.front.map!(d => " %2d".format(d.day));
---------------
If not already present this array should go in std.datetime or
core.time:
static immutable string[] monthNames = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November",
"December"
];
---------------
return to!string(spaces(before) ~ name ~ spaces(after));
==> (untested)
return text(before.spaces, name, after.spaces);
Or maybe even just (untested):
return before.spaces ~ name ~ after.spaces;
---------------
auto formatMonth(Range)(Range monthDays)
if (isInputRange!Range && is(ElementType!Range == Date))
{
assert(!monthDays.empty);
assert(monthDays.front.day == 1);
return chain(
[ monthTitle(monthDays.front.month) ],
monthDays.byWeek().formatWeek());
}
===> (untested)
auto formatMonth(R)(R monthDays)
if (isInputRange!R && is(ElementType!R == Date))
in {
assert(!monthDays.empty);
assert(monthDays.front.day == 1);
} body {
return [monthDays.front.month.monthTitle]
.chain(monthDays.byWeek.formatWeek);
}
Generally I suggest to use pre- and post conditions.
---------------
return months.map!((month) => month.formatMonth());
===> (untested)
return months.map!formatMonth;
---------------
.map!((r) =>
===>
.map!(r =>
---------------
int year = to!int(args[1]);
===>
int year = args[1].to!int;
---------------
On Rosettacode there is a shorter calendar:
http://rosettacode.org/wiki/Calendar#D
If you want we can put, as second D entry, your calendar code
(without unittests) in that page too.
Bye,
bearophile