Perl Weekly Challenge: Week 332

Challenge 1:

Binary Date

You are given a date in the format YYYY-MM-DD.

Write a script to convert it into binary date.

Example 1
Input: $date = "2025-07-26"
Output: "11111101001-111-11010"
Example 2
Input: $date = "2000-02-02"
Output: "11111010000-10-10"
Example 3
Input: $date = "2024-12-31"
Output: "11111101000-1100-11111"

This challenge can done as a one-liner. We take the first command-line argument (@*ARGS[0]) and .split() it into segments at - characters. Then, using .map() we convert each of those segments into numbers with .Int() and then into binary numbers with .base(2). The - separators are then reinserted when the segments are .join()ed up into one string again and the result is printed with .say().

@*ARGS[0].split(q{-}).map({$_.Int.base(2)}).join(q{-}).say

(Full code on Github.)

Perl can also do this in one line. However we have to use sprintf() to do the binary conversion.

say join q{-}, (map {sprintf "%b", $_} split /-/, shift)

(Full code on Github.)

Challenge 2:

Odd Letters

You are given a string.

Write a script to find out if each letter in the given string appeared odd number of times.

Example 1
Input: $str = "weekly"
Output: false

w: 1 time
e: 2 times
k: 1 time
l: 1 time
y: 1 time

The letter 'e' appeared 2 times i.e. even.
Example 2
Input: $str = "perl"
Output: true
Example 3
Input: $source = "challenge"
Output: false

This is also one line in Raku though perhaps a little unintuitive to understand. I would definitely comment this meticulously if it was production code.

We start once again with retrieving the input string from the first command-line argument. This is split into a Sequence of characters with .comb() and then converted into a Bag with .Bag(). A Bag is somewhat like a std::unordered_multiset in C++. Its' keys are the letters in the input, and values, the number of times each key occurs. Now the complicated bit; the values of this Bag is compared to a test condition in an all junction, The condition is %% 2 i.e. what is the remainder if the value is divided by two? For an even number, by definition it's going to be 0 which converts to False. For odd numbers, the remainder will be 1 which converts to True. So we will have a List of True or False elements. all will return all(True) if and only if all elements of the list are True which will only be the case if all the values of the Bag are odd. Otherwise it returns all(False). .so() enforces Boolean context making the answer True or False which is then printed out by .say().

all(@*ARGS[0].comb.Bag.values %% 2).so.say

(Full code on Github.)

In Perl we don't have Bags or junctions so we have to try a different and longer approach.

As in Raku, the input is split() up into individual letters.

my @letters = split //, $str;

A Hash stores the letters as keys and their frequency as values.

my %count;

%count is populated in this loop.

for my $letter (@letters) {
    $count{$letter}++;
}

Now in a second loop, the values of %count are examined and if one should prove to be even, False will be output and the script will end.

for my $n (values %count) {
    if ($n % 2 == 0) {
        say "False";
        exit;
    }
}

If we reach the end of the loop, it means all the values were odd so we print True.

say "True";

(Full code on Github.)