I said that I probably wouldn’t have time to get involved with the Perl Weekly Challenge every week and that has, unfortunately, proven to be the case. But I had a few free minutes earlier in the week so I decided to look at this week’s challenges. I’m glad I did because they seemed to fit the way my brain works pretty well and I had solutions written rather quickly.
Challenge 1: Write a script to display months from the year 1900 to 2019 where you find 5 weekends i.e. 5 Friday, 5 Saturday and 5 Sunday.
This would be simple enough to just brute-force. But when I started to think about it, I realised there’s a bit of a trick we can use which can cut down our search space quite significantly.
If we’re looking for a month with five Fridays, Saturdays and Sundays then we need a month with 31 days (as four weeks is twenty-eight days and we need three extra days). Only seven months ever have 31 days – January, March, May, July, August, October and December. There is no point at all in ever looking in any other month. You might also realise that those three extra days need to be Friday 29th, Saturday 30th and Sunday 31st. And that means that the first day of the month also needs to be a Friday.
So, the problem simplifies to “Find months with 31 days where the 1st is a Friday”. And here’s the code I wrote to do that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#!/usr/bin/perl use strict; use warnings; use feature 'say'; use Time::Piece; # Array of months with 31 days my @months = (qw[Jan Mar May Jul Aug Oct Dec]); my $format = '%Y-%b'; for my $y (1900 .. 2019) { for my $m (@months) { # Get the first day of the month as a Time::Piece object my $first = Time::Piece->strptime("$y-$m", $format); # Print the date if the 1st is a Friday say $first->strftime('%b %Y') if $first->day eq 'Fri'; } } |
I’ve seen a few other solutions published and people seem to split into one group who spotted the shortcuts and another who didn’t. But the actual solutions seem very similar. Some people used DateTime instead of Time::Piece and others used low-level functions like timelocal().
Challenge 2: Write a script that can wrap the given paragraph at a specified column using the greedy algorithm.
Honestly, I didn’t think very hard about this at all. I just read the Wikipedia description of the algorithm and wrote a pretty much word-for-word Perl translation of that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#!/usr/bin/perl use strict; use warnings; use constant SPACE => ' '; use constant SPACE_WIDTH => length(SPACE); my $line_len = shift || 72; # Old-skool Unix my $text = join ' ', <DATA>; my $space_left = $line_len; for (split /\s+/, $text) { if (SPACE_WIDTH + length() > $space_left) { print "\n$_" . SPACE; $space_left = $line_len - length(); } else { print $_ . SPACE; $space_left -= (length() + SPACE_WIDTH); } } print "\n"; __DATA__ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis volutpat, ipsum nec luctus dictum, velit nisi sodales dui, ut feugiat risus dolor vel metus. Morbi ut pretium velit. Proin ultricies enim magna, at semper odio molestie vitae. In hac habitasse platea dictumst. Fusce non sapien bibendum ligula pellentesque volutpat in et lectus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vestibulum sodales molestie dignissim. |
Next week is all about the European Perl Conference so I very much doubt if I’ll have time to try the Perl Weekly Challenges. But I hope to be able to try more of the problems in the coming weeks.
The
strptime
–strftime
loop seems to be overkill if the day of week can be calculated with just two statements:This particular formula comes from http://www.cadaeic.net/calendar.htm but there are others like https://cs.uwaterloo.ca/~alopez-o/math-faq/mathtext/node39.html