Welcome to Day 14 of the Perl 6 One-Liner Advent Calendar! Today, we are presenting another solution of the problem we were solving yesterday. The task was to count all Sundays that fall on the first of the month in the XX century.
Yesterday, we just scanned through all the days in the whole century, selecting the ones that are Sundays (.day-of-week == 7) and are the first of the month (.day == 1).
It is possible to make a more efficient algorithm. As we are only interested in the first days of the month, there is no need to scan all 36525 days during 100 years, but only 1200 days that are the first day of each month between 1901 and 2000.
So, we need two nested loops: over the years and over the months. Do we need two fors? Not necessarily. Let’s use the X operator; we are familiar with it from the previous advent posts.
And here is our today’s one-liner:
(gather for 1901..2000 X 1..12 {
take Date.new(|@_, 1)
}).grep(*.day-of-week == 7).elems.say;
The for 1901..2000 X 1..12 loop goes over each month in the XX century. For each, we create a Date object by calling a constructor with three arguments.
Update. Based on the very relevant comments from the readers, here’s a better and simpler way of the solution:
say +(1901..2000 X 1..12).map(
{Date.new(|@_, 1)}
).grep(*.day-of-week == 7);
Notice that inside the loop, you can use both $_[0] and $_[1], and @_[0] and @_[1]. In the first case, the $_ variable contains a list of two elements, while in the second it is an array @_. The shortest code is achieved if you are just using a dot to call a method on the topic (default) variable: .[0] and .[1].
Instead of Date.new(|@_, 1), you can type Date.new(.[0], .[1], 1). The |@_ syntax is used to unroll the array, as otherwise Perl 6 will think you are passing an array as a first argument.
All first days of the months are collected to a sequence with the help of the gather—take pair.
The final step is a grep as yesterday, but this time we only need to select Sundays, so a single *.day-of-week == 7 condition is enough.
The call of the elems method returns the number of the elements in the list, which is the number of Sundays that we are looking for. Thus, print it with say.
A new idea and a new solution should make you happy for at least one day until tomorrow’s post in this One-Liner Calendar!
The gather-take looks to me a lot like (roughly) map { Date } 19.. X 1.. . Do they have the same effect? Any reason to prefer one or the other? Thanks!
You are so so so right. A map will be much simpler here.
TIMTOWTDI! 🙂