RabbitFarm
2025-06-22
The Weekly Challenge 326 (Prolog Solutions)
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
Part 1: Day of the Year
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.
Our solution is short, it involves just a couple of computations, and will be contained in a single file that has the following structure.
We’ll put the determination of whether a year is a leap year or not into its own predicate.
-
leap_year(Year):-
M1 is Year mod 4,
M2 is Year mod 100,
M3 is Year mod 400,
((M1 == 0, \+ M2 == 0);
(M1 == 0, M2 == 0, M3 == 0)).
◇
-
Fragment referenced in 1.
Similarly, we’ll put the calculation of the number of February days in its own predicate.
-
february_days(Year, Days):-
leap_year(Year),
Days = 29.
february_days(_, Days):-
Days = 28.
◇
-
Fragment referenced in 1.
One more utility predicate, which splits the input into day, month, and year values.
-
day_month_year(S, Day, Month, Year):-
append(Y, [45|T], S),
append(M, [45|D], T),
number_codes(Day, D),
number_codes(Month, M),
number_codes(Year, Y).
◇
-
Fragment referenced in 1.
Finally, let’s compute the day of the year.
-
day_of_year(Date, DayOfYear) :-
day_month_year(Date, Day, Month, Year),
february_days(Year, FebruaryDays),
DaysInMonth = [31, FebruaryDays, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
succ(M, Month),
length(Prefix, M),
prefix(Prefix, DaysInMonth),
sum_list(Prefix, MonthSum),
DayOfYear is MonthSum + Day.
◇
-
Fragment referenced in 1.
Sample Run
$ gprolog --consult-file prolog/ch-1.p | ?- day_of_year("2025-02-02", DayOfYear). DayOfYear = 33 ? yes | ?- day_of_year("2025-04-10", DayOfYear). DayOfYear = 100 ? yes | ?- day_of_year("2025-09-07", DayOfYear). DayOfYear = 250 ? yes | ?-
Part 2: Decompressed List
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.
The code required is fairly small, we’ll just need a couple of predicates.
We’ll define a DCG to “decompress”the list. First, let’s have some predicates for maintaining the state of the decompression as it proceeds.
-
decompression(Decompression), [Decompression] --> [Decompression].
decompression(D, Decompression), [Decompression] --> [D].
◇
-
Fragment referenced in 6.
The DCG for this is not so complex. Mainly we need to be concerned with maintaining the state of the decompression as we process the list.
-
decompress(Input) --> decompression(D, Decompression),
{Input = [I, J|T],
length(L, I),
maplist(=(J), L),
append(D, L, Decompression)
},
decompress(T).
decompress([]) --> [].
◇
-
Fragment referenced in 6.
Finally, let’s wrap the calls to the DCG in a small predicate using phrase/3.
-
decompress_list(L, Decompressed):-
phrase(decompress(L), [[]], [Decompressed]).
◇
-
Fragment referenced in 6.
Sample Run
$ gprolog --consult-file prolog/ch-2.p | ?- decompress_list([1, 3, 2, 4], Decompressed). Decompressed = [3,4,4] yes | ?- decompress_list([1, 1, 2, 2], Decompressed). Decompressed = [1,2,2] yes | ?- decompress_list([3, 1, 3, 2], Decompressed). Decompressed = [1,1,1,2,2,2] yes | ?-
References
posted at: 18:45 by: Adam Russell | path: /prolog | permanent link to this entry
The Day We Decompress
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
Part 1: Day of the Year
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.
The core of the solution is contained in a main loop. The resulting code can be contained in a single file.
The answer is arrived at via a fairly straightforward calculation.
-
sub day_of_year {
my ($date) = @_;
my $day_of_year = 0;
my ($year, $month, $day) = split /-/, $date;
my @days_in_month = (31, $february_days, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
$day_of_year += $days_in_month[$_] for (0 .. $month - 2);
$day_of_year += $day;
return $day_of_year;
}
◇
Let’s break the logic for computing a leap year into it’s own section. A leap year occurs every 4 years, except for years that are divisible by 100, unless they are also divisible by 400.
Just to make sure things work as expected we’ll define a few short tests. The double chop is just a lazy way to make sure there aren’t any trailing commas in the output.
-
MAIN:{
say day_of_year q/2025-02-02/;
say day_of_year q/2025-04-10/;
say day_of_year q/2025-09-07/;
}
◇
-
Fragment referenced in 1.
Sample Run
$ perl perl/ch-1.pl 33 100 250
Part 2: Decompressed List
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.
For fun let’s use recursion!
Sometimes when I write a recursive subroutine in Perl I use a reference variable to set the return value. Other times I just use an ordinary return. In some cases, for convenience, I’ll do this with two subroutines. One of these is a wrapper which calls the main recursion.
For this problem I’ll do something a little different. I’ll have one subroutine and for each recursive call I’ll add in an array reference to hold the accumulating return value.
Note that we take advantage of Perl’s automatic list flattening when pushing to the array reference holding the new list we are building.
-
sub decompress_list{
my $r = shift @_;
if(
!ref($r) || ref($r) ne q/ARRAY/){
unshift @_, $r;
$r = [];
}
unless(@_ == 0){
my $i = shift @_;
my $j = shift @_;
push @{$r}, ($j) x $i;
decompress_list($r, @_);
}
else{
return @{$r};
}
}
◇
-
Fragment referenced in 5.
The main section is just some basic tests.
-
MAIN:{
say join q/, /, decompress_list 1, 3, 2, 4;
say join q/, /, decompress_list 1, 1, 2, 2;
say join q/, /, decompress_list 3, 1, 3, 2;
}
◇
-
Fragment referenced in 5.
Sample Run
$ perl perl/ch-2.pl 3, 4, 4 1, 2, 2 1, 1, 1, 2, 2, 2
References
posted at: 16:45 by: Adam Russell | path: /perl | permanent link to this entry