Perl Weekly Challenge 326.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 326.
Task 1: Day of the Year
Submitted by: Mohammad Sajid Anwar
You are given a date in the format YYYY-MM-DD.
Write a script to find day number of the year that the given date represent.
Example 1
Input: $date = '2025-02-02'
Output: 33
The 2nd Feb, 2025 is the 33rd day of the year.
Example 2
Input: $date = '2025-04-10'
Output: 100
Example 3
Input: $date = '2025-09-07'
Output: 250
According to the Gregorian calendar, if the year number is a multiple of 4 the year is a leap year, unless it is a multiple of 100, when it is not a leap year, unless it is a multiple of 400, when it is. So it is simple to find out if February within a given year has 28 or 29 days. First I compute a list of days to the beginning of each month in ordinary years:
perl -MList::Util=reductions -E '
@m=(0,31,28,31,30,31,30,31,31,30,31,30,31); say join ",",reductions {$a+$b} @m;'
Results:
0,31,59,90,120,151,181,212,243,273,304,334,365
I used reductions
from List::Util
to add all days up to the
beginning of each month.
With that list I can easily parse the ISO date obtain the starting day number for the month, add the day of the month, and add 1 after February for leap years. The result fits a two liner.
Examples:
perl -E '
@s=(0,0,31,59,90,120,151,181,212,243,273,304,334);for(@ARGV){($y,$m,$d)=split "-";
say "$_ -> ", $s[$m]+$d+($m>2&&$_%4==0&&($_%100||$_%400==0)?1:0);}
' 2025-02-02 2025-04-10 2025-09-07
Results:
2025-02-02 -> 33
2025-04-10 -> 100
2025-09-07 -> 250
Unfortunately, these examples don’t test leap years, as 2025 is not multiple of 4. So I make a few additional examples:
perl -E '
@s=(0,0,31,59,90,120,151,181,212,243,273,304,334);for(@ARGV){($y,$m,$d)=split "-";
say "$_ -> ", $s[$m]+$d+($m>2&&$_%4==0&&($_%100||$_%400==0)?1:0);}
' 2024-02-01 2024-03-01 2025-03-01 2100-03-01 2400-03-01
Results:
2024-02-01 -> 32
2024-03-01 -> 61
2025-03-01 -> 60
2100-03-01 -> 60
2400-03-01 -> 61
The first line shows that being in a leap year doesn’t affect dates in February, the second shows that it does affect March, as verified by a comparison to the third line, as 2025 is not a leap year, not being a multiple of 4. The fourth line is consistent with 2100 not being a leap year, even as it is a multiple of 4, since it is also a multiple of 100. Finally, the fifth line is consistent with 2400 being a leap year, even as it is a multiple of 100, since it is also a multiple of 400.
The full code is less obscure and adds a few tests.
1 # Perl weekly challenge 326
2 # Task 1: Day of the Year
3 #
4 # See https://wlmb.github.io/2025/06/16/PWC326/#task-1-day-of-the-year
5 use v5.36;
6 use List::Util qw(reductions);
7 die <<~"FIN" unless @ARGV;
8 Usage: $0 D1 D2...
9 to find the day number corresponding to the ISO dates D1, D2.
10 FIN
11 my @days_in_month=(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
12 my @offsets=(0, reductions {$a+$b} @days_in_month);
13 for(@ARGV){
14 say("Wrong format: $_"), next unless /^(\d{4})-(\d{2})-(\d{2})$/;
15 my ($year, $month, $day_of_month)=($1, $2, $3);
16 say("Wrong month: $_"), next unless 1 <= $month <= 12;
17 say("Wrong day: $_"), next
18 unless 1 <= $day_of_month
19 && ($day_of_month <= $days_in_month[$month] || $month == 2 && $day_of_month <= 29);
20 my $leap=$year%4==0;
21 $leap=0 if $year%100 == 0;
22 $leap=1 if $year%400 == 0;
23 my $day_of_year = $offsets[$month] + $day_of_month;
24 ++$day_of_year if $leap and $month >= 3;
25 say "$_ -> $day_of_year";
26 }
27
Example:
./ch-1.pl 2025-02-02 2025-04-10 2025-09-07 2024-02-01 2024-03-01 2025-03-01 2100-03-01 2400-03-01
Results:
2025-02-02 -> 33
2025-04-10 -> 100
2025-09-07 -> 250
2024-02-01 -> 32
2024-03-01 -> 61
2025-03-01 -> 60
2100-03-01 -> 60
2400-03-01 -> 61
Task 2: Decompressed List
Submitted by: Mohammad Sajid Anwar
You are given an array of positive integers having even elements.
Write a script to to return the decompress list. To decompress, pick
adjacent pair (i, j) and replace it with j, i times.
Example 1
Input: @ints = (1, 3, 2, 4)
Output: (3, 4, 4)
Pair 1: (1, 3) => 3 one time => (3)
Pair 2: (2, 4) => 4 two times => (4, 4)
Example 2
Input: @ints = (1, 1, 2, 2)
Output: (1, 2, 2)
Pair 1: (1, 1) => 1 one time => (1)
Pair 2: (2, 2) => 2 two times => (2, 2)
Example 3
Input: @ints = (3, 1, 3, 2)
Output: (1, 1, 1, 2, 2, 2)
Pair 1: (3, 1) => 1 three times => (1, 1, 1)
Pair 2: (3, 2) => 2 three times => (2, 2, 2)
I use for_list
to iterate over the i,j
pairs. I use the repetation
operator x
to build the resulting list. This yields a half-liner.
Example 1:
perl -E '
for my($i,$j)(@ARGV){push @o, ($j)x$i} say "@ARGV -> @o"
' 1 3 2 4
Results:
1 3 2 4 -> 3 4 4
Example 2:
perl -E '
for my($i,$j)(@ARGV){push @o, ($j)x$i} say "@ARGV -> @o"
' 1 1 2 2
Results:
1 1 2 2 -> 1 2 2
Example 3:
perl -E '
for my($i,$j)(@ARGV){push @o, ($j)x$i} say "@ARGV -> @o"
' 3 1 3 2
Results:
3 1 3 2 -> 1 1 1 2 2 2
The full code is similar.
1 # Perl weekly challenge 326
2 # Task 2: Decompressed List
3 #
4 # See https://wlmb.github.io/2025/06/16/PWC326/#task-2-decompressed-list
5 use v5.36;
6 die <<~"FIN" unless @ARGV and @ARGV%2==0;
7 Usage: $0 R1 N1 R2 N2...
8 to make a list with number Ni repeated Ri times.
9 FIN
10 my @output=();
11 for my($i,$j)(@ARGV){
12 push @output, ($j)x$i;
13 }
14 say "@ARGV -> @output"
Examples:
./ch-2.pl 1 3 2 4
./ch-2.pl 1 1 2 2
./ch-2.pl 3 1 3 2
Results:
1 3 2 4 -> 3 4 4
1 1 2 2 -> 1 2 2
3 1 3 2 -> 1 1 1 2 2 2
/;