Perl Weekly Challenge: Oddly Binary

With this PWC evoking “oddness”, the only music I could really use was David Bowie’s Space Oddity.

So let’s sit in our tin can far above the world and watch Perl Weekly Challenge 332 unfold.

Task 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"

Approach

This is pretty straightforward: split the string on "-" into a list, then convert each of those numbers into the binary representation, then rejoin them. Easy peasy.

Raku

Ah, but what we get when we split isn’t a number, it’s a string. So we need to convert them to an integer first. Fortunately, the Str class has a .Int method to do that.

sub binaryDate($date) {
  $date.split(/\-/).map({ .Int.base(2) }).join("-");
}

View the entire Raku script for this task on GitHub.

$ raku/ch-1.raku
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"

Perl

There’s probably a better way to convert a number into its binary form in Perl, but immediately my mind went to sprintf("%b"). The great thing is Perl’s automatic type conversion meant I didn’t need to convert the string into an integer before passing it to sprintf.

sub binaryDate($date) {
  join "-", map { sprintf "%b", $_ } split /\-/, $date;
}

View the entire Perl script for this task on GitHub.

Python

Python proved to have a wrinkle:

>>> '-'.join([ bin(int(i)) for i in "2025-07-26".split('-')])
'0b11111101001-0b111-0b11010'

Those 0b prefixes in front of each number.So I needed to lop them off.

def binary_date(date):
  return '-'.join([ bin(int(i))[2:] for i in date.split('-')])

View the entire Python script for this task on GitHub.

Elixir

And Elixir had it’s own wrinkle. The function for converting an integer to it’s binary representation, Integer.digits/2, doesn’t produce a string representation of the digits… it produces a List. Fortunately, piping the list through Enum.join/2 easily rejoins the list into a string.

But Elixir’s pipe operator (|>) makes this easy to read: the string is split, and then we convert each part of the string to an integer from a string, then we convert each integer into its binary representation, and then we rejoin those with a "-".

def binary_date(date) do
  date
  |> String.split("-")
  |> Enum.map(fn s -> String.to_integer(s) end)
  |> Enum.map(fn i -> Integer.digits(i, 2) |> Enum.join end)
  |> Enum.join("-")
end

View the entire Elixir script for this task on GitHub.


Task 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

Approach

Ooh! This is a Bag. We use the bag to count how often each letter appears, then check to see if those counts are even.

Raku

Again, I was able to chain these together: .comb to split the string into its characters, .Bag to populate an immutable Bag object, then we call the .values method to get the values (we don’t actually care about the keys, only whether they appear an odd or even amount of times:

sub oddLetters($str) {
  for $str.comb.Bag.values -> $times {
    return 'false' if $times % 2 == 0;
  }
  return 'true';
}

View the entire Raku script for this task on GitHub.

$ raku/ch-2.raku
Example 1:
Input: $str = "weekly"
Output: false

Example 2:
Input: $str = "perl"
Output: true

Example 3:
Input: $str = "challenge"
Output: false

Perl

In Perl, we have to implement the Bag as a hash…

sub oddLetters($str) {
  my %letters;
  map { $letters{$_}++ } split //, $str;
  foreach my $times (values %letters) {
    return 'false' if $times % 2 == 0;
  }
  return 'true';
}

View the entire Perl script for this task on GitHub.

Python

Once again, the Counter datatype in the collections module is essentially the same as a Bag.

from collections import Counter

def odd_letters(str_val):
  for k,v in Counter(list(str_val)).items():
    if v % 2 == 0: return 'false'
  return 'true'

View the entire Python script for this task on GitHub.

Elixir

I decided to borrow the function I wrote for PWC 283 to actually make the bag, which made the meat of this solution on line 22 really concise. And because I wanted to be able to bail on the loop through the values early if I encountered an even count, the easiest way to do that was to implement a recursive all_odd function that checks the first element of the list, and if it’s even, returns “false”, and then passes the rest of the list to itself, and if there’s no elements left, it returns “true”.

  def make_bag(list) do
    {_, bag} = Enum.map_reduce(list, %{}, fn i, bag ->
      { i, Map.put(bag, i, Map.get(bag, i, 0) + 1) }
    end)
    bag
  end

  def all_odd([]), do: "true"

  def all_odd([this | rest]) do
    if rem(this,2) == 0 do
      "false"
    else
      all_odd(rest)
    end
  end

  def odd_letters(str) do
    str |> String.graphemes |> make_bag |> Map.values |> all_odd
  end

View the entire Elixir script for this task on GitHub.


Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-332/packy-anderson