The second task of last week's Weekly Challenge

was to list the dates of the last Friday in every month of a given year.

Many of the participants went to great lengths to create efficient and accurate solutions:

tracking the last day of each month, detecting leap years correctly,

working out formulas for the day of the month for a particular date,

managing the complex date arithmetic required.

But, given the powerful Date class built into Raku, most of those

admirable exertions were not actually necessary.

The entire task could be accomplished simply by walking through

from 1 January to 31 December of the given year, first checking

if each date is a Friday (i.e. the "day of the week" value is 5), and then

checking if the following Friday (i.e. the date exactly one week later)

happens to fall in a different month.

If both of those criteria are true, then we have a "last Friday of the month",

so we simply print it.

In other words:

for Date.new($year,1,1) .. Date.new($year,12,31) -> $date {

if $date.day-of-week == 5

&& $date.later(:1week).month != $date.month {

say $date;

}

}

We don't need to explicitly worry how long each month is, whether the year is a leap

year, or how to work out whether a given day is a Friday; the Date class takes

care of all that for us.

And that's not luck; it's entirely by design. Raku has

a huge number of built-in datatypes like this;

they're yet another kind of "right tool, right at hand".

Of course, because Raku is multiparadigm, there are plenty of other ways

to wrap a solution around the conveniences of the Date class,

depending on how you prefer to think about the world.

If you like pure functional solutions, write it like so:

say join "

",

grep { .later(:1week).month != .month },

grep { .day-of-week == 5 },

Date.new($year,1,1) .. Date.new($year,12,31);

Or you'd prefer a (potentially) parallel pipeline,

then filter the sequence of dates through a series of

independent filters and processors:

Date.new($year,1,1) .. Date.new($year,12,31)

==> grep( {.day-of-week == 5} )

==> grep( {.later(:1week).month != .month} )

==> join( "

" )

==> say();

Or you'd rather use a purely OO approach, just call the appropriate methods

on the list of dates:

(Date.new($year,1,1) .. Date.new($year,12,31))

.grep( {.day-of-week == 5} )

.grep( {.later(:1week).month != .month} )

.join( "

" )

.say;

You could even write it as an old-school imperative one-liner:

.say if .day-of-week == 5

&& .later(:1week).month != .month

for Date.new($year,1,1) .. Date.new($year,12,31);



So, whether you prefer an imperative, functional, object-oriented,

or pipelined approach, you can solve this problem simply and

cleanly. Because Raku has the necessary built-in data types

to do the heavy lifting for you.

Damian

