Packy Anderson
banner
packy.calckey.world.ap.brid.gy
Packy Anderson
@packy.calckey.world.ap.brid.gy
(he/him) Packy's just 'zis guy, you know? #programmer #SoftwareDeveloper #opensource #terminal #perl #bash #git #freebsd #firefish #fosstodon #mastodon #apple […]

[bridged from https://calckey.world/@packy on the fediverse by https://fed.brid.gy/ ]
Reposted by Packy Anderson
Perl Weekly Challenge: Ok, swing code… SWING!

"Validate Coupon" and "Max Words" are the tasks for Perl Weekly Challenge 353. I can't believe I've done tasks with Max before and it never occurred to me that I needed to feature a song with Max Headroom. Ok, swing code... swing! […]
Original post on fosstodon.org
fosstodon.org
December 23, 2025 at 5:47 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Doodling with matches and prefixes

Even though Perl Weekly Challenge 352's tasks "Match String" and "Binary Prefix" ought to have been fertile ground for a musical theme related to the tasks, my brain was just too preoccupied with a certain someone's 100th birthday […]
Original post on fosstodon.org
fosstodon.org
December 17, 2025 at 4:37 AM
Reposted by Packy Anderson
Perl Weekly Challenge: A pretty average progression…

Since Perl Weekly Challenge 351's tasks are "Special Average" and "Arithmetic Progression", I decided that this week's musical theme should be Average Person by Paul McCartney.

https://packy.dardan.com/b/fQ
Perl Weekly Challenge: A pretty average progression…
Since Perl Weekly Challenge 351‘s tasks are “Special Average” and “Arithmetic Progression”, I decided that this week’s musical theme should be _Average Person_ by Paul McCartney. * Task 1: Special Average * Approach * Raku * Perl * Python * Elixir * Task 2: Arithmetic Progression * Approach * Raku * Perl * Python * Elixir # Task 1: Special Average You are given an array of integers. Write a script to return the average excluding the minimum and maximum of the given array. **Example 1** Input: @ints = (8000, 5000, 6000, 2000, 3000, 7000) Output: 5250 Min: 2000 Max: 8000 Avg: (3000+5000+6000+7000)/4 = 21000/4 = 5250 **Example 2** Input: @ints = (100_000, 80_000, 110_000, 90_000) Output: 95_000 Min: 80_000 Max: 110_000 Avg: (100_000 + 90_000)/2 = 190_000/2 = 95_000 **Example 3** Input: @ints = (2500, 2500, 2500, 2500) Output: 0 Min: 2500 Max: 2500 Avg: 0 **Example 4** Input: @ints = (2000) Output: 0 Min: 2000 Max: 2000 Avg: 0 **Example 5** Input: @ints = (1000, 2000, 3000, 4000, 5000, 6000) Output: 3500 Min: 1000 Max: 6000 Avg: (2000 + 3000 + 4000 + 5000)/4 = 14000/4 = 3500 ## Approach This task is pretty straightforward: one pass though the list to find the min and max values, then a second pass through to remove all values that match those min and max values, and then we count and sum them to generate an average. ## Raku Of course, once I started writing, I just naturally fell into using `min` and `max` to find the minimum and maximum values, and those are probably implemented in the compiler with loops, so I guess I’m doing three passes through the list. sub specialAverage(@ints) { my $min = @ints.min; # ok, using built-in functions to my $max = @ints.max; # find min/max rather than a loop @ints = @ints.grep({ $_ != $min && $_ != $max }); return 0 if @ints.elems == 0; return @ints.sum / @ints.elems; } sub specialAverage(@ints) { my $min = @ints.min; # ok, using built-in functions to my $max = @ints.max; # find min/max rather than a loop @ints = @ints.grep({ $_ != $min && $_ != $max }); return 0 if @ints.elems == 0; return @ints.sum / @ints.elems; } View the entire Raku script for this task on GitHub. $ raku/ch-1.raku Example 1: Input: @ints = (8000, 5000, 6000, 2000, 3000, 7000) Output: 5250 Example 2: Input: @ints = (100000, 80000, 110000, 90000) Output: 95000 Example 3: Input: @ints = (2500, 2500, 2500, 2500) Output: 0 Example 4: Input: @ints = (2000) Output: 0 Example 5: Input: @ints = (1000, 2000, 3000, 4000, 5000, 6000) Output: 3500 $ raku/ch-1.raku Example 1: Input: @ints = (8000, 5000, 6000, 2000, 3000, 7000) Output: 5250 Example 2: Input: @ints = (100000, 80000, 110000, 90000) Output: 95000 Example 3: Input: @ints = (2500, 2500, 2500, 2500) Output: 0 Example 4: Input: @ints = (2000) Output: 0 Example 5: Input: @ints = (1000, 2000, 3000, 4000, 5000, 6000) Output: 3500 ## Perl And because Perl has a plethora of CPAN modules descended from `List::Util` that provide `min`, `max`, and `sum`, I just used those from my favorite, `List::AllUtils`. use List::AllUtils qw( min max sum ); sub specialAverage(@ints) { my $min = min @ints; # ok, using List::Util functions to my $max = max @ints; # find min/max rather than a loop @ints = grep { $_ != $min && $_ != $max } @ints; return 0 if @ints == 0; return sum(@ints) / scalar(@ints); } use List::AllUtils qw( min max sum ); sub specialAverage(@ints) { my $min = min @ints; # ok, using List::Util functions to my $max = max @ints; # find min/max rather than a loop @ints = grep { $_ != $min && $_ != $max } @ints; return 0 if @ints == 0; return sum(@ints) / scalar(@ints); } View the entire Perl script for this task on GitHub. ## Python Python, just like Raku, has the `min`, `max`, and `sum` functions built in. def special_average(ints): minv = min(ints) # ok, using built-in functions to maxv = max(ints) # find min/max rather than a loop ints = [ i for i in ints if i != minv and i != maxv ] if len(ints) == 0: return 0 return int(sum(ints) / len(ints)) def special_average(ints): minv = min(ints) # ok, using built-in functions to maxv = max(ints) # find min/max rather than a loop ints = [ i for i in ints if i != minv and i != maxv ] if len(ints) == 0: return 0 return int(sum(ints) / len(ints)) View the entire Python script for this task on GitHub. ## Elixir And because the algorithm I’m implementing doesn’t have any early bailing out of these loops, I’m able to write it all up in a single, non-recursive Elixir function: def special_average(ints) do min = Enum.min(ints) # ok, using built-in functions to max = Enum.max(ints) # find min/max rather than a loop ints = Enum.filter(ints, fn i -> i != min and i != max end) if length(ints) == 0 do 0 else trunc(Enum.sum(ints) / length(ints)) end end def special_average(ints) do min = Enum.min(ints) # ok, using built-in functions to max = Enum.max(ints) # find min/max rather than a loop ints = Enum.filter(ints, fn i -> i != min and i != max end) if length(ints) == 0 do 0 else trunc(Enum.sum(ints) / length(ints)) end end View the entire Elixir script for this task on GitHub. * * * # Task 2: Arithmetic Progression You are given an array of numbers. Write a script to return true if the given array can be re-arranged to form an arithmetic progression, otherwise return false. > A sequence of numbers is called an arithmetic progression if the difference between any two consecutive elements is the same. **Example 1** Input: @num = (1, 3, 5, 7, 9) Output: true Already AP with common difference 2. **Example 2** Input: @num = (9, 1, 7, 5, 3) Output: true The given array re-arranged like (1, 3, 5, 7, 9) with common difference 2. **Example 3** Input: @num = (1, 2, 4, 8, 16) Output: false This is geometric progression and not arithmetic progression. **Example 4** Input: @num = (5, -1, 3, 1, -3) Output: true The given array re-arranged like (-3, -1, 1, 3, 5) with common difference 2. **Example 5** Input: @num = (1.5, 3, 0, 4.5, 6) Output: true The given array re-arranged like (0, 1.5, 3, 4.5, 6) with common difference 1.5. ## Approach We’re going to sort the elements from lowest to highest, then calculate the difference between the first two elements and make sure the differences between each subsequent pair matches. ## Raku At first, I pulled off the first two elements to calculate the diff, but then I realized I could save a line by just leaving the first value of `$cur` in the `@ints` list and just re-calculate the difference again the first time through the loop. sub arithmeticProgression(@ints) { @ints = @ints.sort; my $prev = @ints.shift; my $diff = @ints[0] - $prev; # calc first difference for @ints -> $cur { return False if $diff != $cur - $prev; $prev = $cur; } return True; } sub arithmeticProgression(@ints) { @ints = @ints.sort; my $prev = @ints.shift; my $diff = @ints[0] - $prev; # calc first difference for @ints -> $cur { return False if $diff != $cur - $prev; $prev = $cur; } return True; } View the entire Raku script for this task on GitHub. $ raku/ch-2.raku Example 1: Input: @ints = (1, 3, 5, 7, 9) Output: True Example 2: Input: @ints = (9, 1, 7, 5, 3) Output: True Example 3: Input: @ints = (1, 2, 4, 8, 16) Output: False Example 4: Input: @ints = (5, -1, 3, 1, -3) Output: True Example 5: Input: @ints = (1.5, 3, 0, 4.5, 6) Output: True $ raku/ch-2.raku Example 1: Input: @ints = (1, 3, 5, 7, 9) Output: True Example 2: Input: @ints = (9, 1, 7, 5, 3) Output: True Example 3: Input: @ints = (1, 2, 4, 8, 16) Output: False Example 4: Input: @ints = (5, -1, 3, 1, -3) Output: True Example 5: Input: @ints = (1.5, 3, 0, 4.5, 6) Output: True ## Perl The big difference between Raku and Perl is that we need to _explicitly_ say we’re sorting `@ints` numerically. sub arithmeticProgression(@ints) { @ints = sort { $a <=> $b } @ints; my $prev = shift @ints; my $diff = $ints[0] - $prev; # calc first difference foreach my $cur (@ints) { return 'False' if $diff != $cur - $prev; $prev = $cur; } return 'True'; } sub arithmeticProgression(@ints) { @ints = sort { $a <=> $b } @ints; my $prev = shift @ints; my $diff = $ints[0] - $prev; # calc first difference foreach my $cur (@ints) { return 'False' if $diff != $cur - $prev; $prev = $cur; } return 'True'; } View the entire Perl script for this task on GitHub. ## Python There’s not much to say about the Python version. def arithmetic_progression(ints): ints = sorted(ints) prev = ints.pop(0) diff = ints[0] - prev # calc first difference for cur in ints: if diff != cur - prev: return False prev = cur return True def arithmetic_progression(ints): ints = sorted(ints) prev = ints.pop(0) diff = ints[0] - prev # calc first difference for cur in ints: if diff != cur - prev: return False prev = cur return True View the entire Python script for this task on GitHub. ## Elixir However, because this algorithm **does** involve bailing out of the loop early if we fail a condition, I’ve implemented it as a recursive function. The assignment of `prev = cur` winds up happening on line 9 when we recursively call `arithmetic_progression/3` with `cur` instead of `prev`. def arithmetic_progression([], _, _), do: true def arithmetic_progression([cur | ints], prev, diff) do cond do diff != cur - prev -> false true -> arithmetic_progression(ints, cur, diff) end end def arithmetic_progression(ints) do ints = Enum.sort(ints) {prev, ints} = {hd(ints), tl(ints)} diff = hd(ints) - prev # calc first difference arithmetic_progression(ints, prev, diff) end def arithmetic_progression([], _, _), do: true def arithmetic_progression([cur | ints], prev, diff) do cond do diff != cur - prev -> false true -> arithmetic_progression(ints, cur, diff) end end def arithmetic_progression(ints) do ints = Enum.sort(ints) {prev, ints} = {hd(ints), tl(ints)} diff = hd(ints) - prev # calc first difference arithmetic_progression(ints, prev, diff) 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-351/packy-anderson ### _Related_
packy.dardan.com
December 10, 2025 at 5:33 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Shuffling the Good

With PWC 350's tasks being "Good Substrings" and "Shuffle Pairs", I really couldn't avoid the musical theme being Boz Scaggs' Lido Shuffle. So let's do one more for the road in the Perl Weekly Challenge.

https://packy.dardan.com/b/er
Perl Weekly Challenge: Shuffling the Good
With PWC 350‘s tasks being “Good Substrings” and “Shuffle Pairs”, I really couldn’t avoid the musical theme being Boz Scaggs’ _Lido Shuffle_. So let’s do one more for the road in the Perl Weekly Challenge. * Task 1: Good Substrings * Approach * Raku * Perl * Python * Elixir * Task 2: Shuffle Pairs * Approach * Raku * Perl * Python * Elixir # Task 1: Good Substrings You are given a string. Write a script to return the number of good substrings of length three in the given string. > A string is good if there are no repeated characters. **Example 1** Input: $str = "abcaefg" Output: 5 Good substrings of length 3: abc, bca, cae, aef and efg **Example 2** Input: $str = "xyzzabc" Output: 3 Good substrings of length 3: "xyz", "zab" and "abc" **Example 3** Input: $str = "aababc" Output: 1 Good substrings of length 3: "abc" **Example 4** Input: $str = "qwerty" Output: 4 Good substrings of length 3: "qwe", "wer", "ert" and "rty" **Example 5** Input: $str = "zzzaaa" Output: 0 ## Approach I think this is a good use for a bag: loop over the string and extract three-character substrings, and then use a bag to determine if any of the characters occur more than once. ## Raku We can’t push the substring into the Bag to count the characters, we have to break the substring into its composite characters. I used `.comb` to do that, and then `.values` to get the character counts and `.any` to determine if any of those counts were greater than 1. sub goodString($str) { my $good = 0; for 0 .. $str.chars - 3 -> $i { my $substr = $str.substr($i..$i+2); my %bag = $substr.comb().Bag; $good++ unless %bag.values.any > 1; } return $good; } sub goodString($str) { my $good = 0; for 0 .. $str.chars - 3 -> $i { my $substr = $str.substr($i..$i+2); my %bag = $substr.comb().Bag; $good++ unless %bag.values.any > 1; } return $good; } View the entire Raku script for this task on GitHub. $ raku/ch-1.raku Example 1: Input: $str = "abcaefg" Output: 5 Example 2: Input: $str = "xyzzabc" Output: 3 Example 3: Input: $str = "aababc" Output: 1 Example 4: Input: $str = "qwerty" Output: 4 Example 5: Input: $str = "zzzaaa" Output: 0 $ raku/ch-1.raku Example 1: Input: $str = "abcaefg" Output: 5 Example 2: Input: $str = "xyzzabc" Output: 3 Example 3: Input: $str = "aababc" Output: 1 Example 4: Input: $str = "qwerty" Output: 4 Example 5: Input: $str = "zzzaaa" Output: 0 ## Perl I’m once again stealing Matthias Muth‘s idea of using List::MoreUtils’ `frequency` to create a bag/multiset, as well as importing `any` from List::MoreUtils as well. Otherwise, it’s pretty much the same as the Raku solution. use List::MoreUtils qw( any frequency ); sub goodString($str) { my $good = 0; foreach my $i (0 .. length($str) - 3) { my $substr = substr($str, $i, $i+2); my %bag = frequency split //, $substr; $good++ unless any { $_ > 1 } values %bag; } return $good; } use List::MoreUtils qw( any frequency ); sub goodString($str) { my $good = 0; foreach my $i (0 .. length($str) - 3) { my $substr = substr($str, $i, $i+2); my %bag = frequency split //, $substr; $good++ unless any { $_ > 1 } values %bag; } return $good; } View the entire Perl script for this task on GitHub. ## Python As usual, I’m using `collections's Counter` to make our multiset, and the built-in `any()` to mke sure none of the counts were greater than 1. from collections import Counter def good_string(mystr): good = 0 for i in range(len(mystr)-2): substr = mystr[i:i+3] bag = Counter(list(substr)) if not any([x > 1 for x in bag.values()]): good += 1 return good from collections import Counter def good_string(mystr): good = 0 for i in range(len(mystr)-2): substr = mystr[i:i+3] bag = Counter(list(substr)) if not any([x > 1 for x in bag.values()]): good += 1 return good View the entire Python script for this task on GitHub. ## Elixir Once again, I’m using recursion to do my looping, and `Enum.frequencies/1` to make my bag/multiset. def good_string(_, good, i, m) when i >= m, do: good def good_string(str, good, i, m) do bag = str |> String.slice(i..i+2) # get the substring |> String.graphemes # split into characters |> Enum.frequencies # make the map good = if Enum.any?(Map.values(bag), fn x -> x > 1 end) do good else good+1 end good_string(str, good, i+1, m) end def good_string(str) do good_string(str, 0, 0, String.length(str) - 2) end def good_string(_, good, i, m) when i >= m, do: good def good_string(str, good, i, m) do bag = str |> String.slice(i..i+2) # get the substring |> String.graphemes # split into characters |> Enum.frequencies # make the map good = if Enum.any?(Map.values(bag), fn x -> x > 1 end) do good else good+1 end good_string(str, good, i+1, m) end def good_string(str) do good_string(str, 0, 0, String.length(str) - 2) end View the entire Elixir script for this task on GitHub. * * * # Task 2: Shuffle Pairs If two integers `A <= B` have the same digits but in different orders, we say that they belong to the same _shuffle pair_ if and only if there is an integer `k` such that `A = B * k`. `k` is called the _witness_ of the pair. For example, 1359 and 9513 belong to the same shuffle pair, because `1359 * 7 = 9513`. Interestingly, some integers belong to several different shuffle pairs. For example, 123876 forms one shuffle pair with 371628, and another with 867132, as `123876 * 3 = 371628`, and `123876 * 7 = 867132`. Write a function that for a given `$from`, `$to`, and `$count` returns the number of integers `$i` in the range `$from <= $i <= $to` that belong to at least `$count` different shuffle pairs. **PS:** Inspired by a conversation between `Mark Dominus` and `Simon Tatham` at **Mastodon**. **Example 1** Input: $from = 1, $to = 1000, $count = 1 Output: 0 There are no shuffle pairs with elements less than 1000. **Example 2** Input: $from = 1500, $to = 2500, $count = 1 Output: 3 There are 3 integers between 1500 and 2500 that belong to shuffle pairs. 1782, the other element is 7128 (witness 4) 2178, the other element is 8712 (witness 4) 2475, the other element is 7425 (witness 3) **Example 3** Input: $from = 1_000_000, $to = 1_500_000, $count = 5 Output: 2 There are 2 integers in the given range that belong to 5 different shuffle pairs. 1428570 pairs with 2857140, 4285710, 5714280, 7142850, and 8571420 1429857 pairs with 2859714, 4289571, 5719428, 7149285, and 8579142 The witnesses are 2, 3, 4, 5, and 6 for both the integers. **Example 4** Input: $from = 13_427_000, $to = 14_100_000, $count = 2 Output: 11 6 integers in the given range belong to 3 different shuffle pairs, 5 integers belong to 2 different ones. **Example 5** Input: $from = 1030, $to = 1130, $count = 1 Output: 2 There are 2 integers between 1020 and 1120 that belong to at least one shuffle pair: 1035, the other element is 3105 (witness k = 3) 1089, the other element is 9801 (witness k = 9) ## Approach I’m sure there’s some clever way to do this, but I can’t think of it. I’m thinking that the best way to handle this is to loop over the values between `$from` and `$to`, generate their multiples with increasing witness values (`$k`) and see if any of those are shuffles of the starting value. I’m checking for shuffles by sorting the digits in each number to see if they’re the same (i.e., 3421 and 2413 would both, when their digits are sorted, become 1234, so they’re shuffled versions of each other) and keep going until the number of digits in `$A * $k` is larger than the number of digits in `$A` (meaning it can’t be a shuffled version of `$A` because it has too many digits). ## Raku In Raku, I made helper functions to do the length comparison and detecting if the numbers are potential shuffles of each other. Then I put the logic for checking a particular number into a `hasShufflePair` function. The script, however, takes over a minute to run! sub isSameLength($A, $B) { $A.Str.chars == $B.Str.chars; } sub isPair($A, $B) { $A.comb.sort.join eq $B.comb.sort.join; } sub hasShufflePair($A, $min) { my $count = 0; my $k = 2; while (True) { my $B = $A * $k++; # stop processing $A if $B has more digits than $A return 0 unless isSameLength($A, $B); # go to the next $k if $A & $B aren't combinations # of the same digits next unless isPair($A, $B); # it's a shuffle pair, count it if (++$count >= $min) { # abort searching if we found the minimum count of pairs return 1; } } return 0; } sub shufflePairs($from, $to, $count) { sum([$from .. $to].map({hasShufflePair($_, $count)})); } sub isSameLength($A, $B) { $A.Str.chars == $B.Str.chars; } sub isPair($A, $B) { $A.comb.sort.join eq $B.comb.sort.join; } sub hasShufflePair($A, $min) { my $count = 0; my $k = 2; while (True) { my $B = $A * $k++; # stop processing $A if $B has more digits than $A return 0 unless isSameLength($A, $B); # go to the next $k if $A & $B aren't combinations # of the same digits next unless isPair($A, $B); # it's a shuffle pair, count it if (++$count >= $min) { # abort searching if we found the minimum count of pairs return 1; } } return 0; } sub shufflePairs($from, $to, $count) { sum([$from .. $to].map({hasShufflePair($_, $count)})); } $ time raku/ch-2.raku Example 1: Input: $from = 1, $to = 1000, $count = 1 Output: 0 Example 2: Input: $from = 1500, $to = 2500, $count = 1 Output: 3 Example 3: Input: $from = 1000000, $to = 1500000, $count = 5 Output: 2 Example 4: Input: $from = 13427000, $to = 14100000, $count = 2 Output: 11 Example 5: Input: $from = 1030, $to = 1130, $count = 1 Output: 2 real 1m13.067s user 1m12.044s sys 0m0.267s $ time raku/ch-2.raku Example 1: Input: $from = 1, $to = 1000, $count = 1 Output: 0 Example 2: Input: $from = 1500, $to = 2500, $count = 1 Output: 3 Example 3: Input: $from = 1000000, $to = 1500000, $count = 5 Output: 2 Example 4: Input: $from = 13427000, $to = 14100000, $count = 2 Output: 11 Example 5: Input: $from = 1030, $to = 1130, $count = 1 Output: 2 real 1m13.067s user 1m12.044s sys 0m0.267s But, in Raku, there’s a way to process these in parallel to produce faster results. sub shufflePairs($from, $to, $count) { sum([$from .. $to].race(:degree(16)) .map({hasShufflePair($_, $count)})); } sub shufflePairs($from, $to, $count) { sum([$from .. $to].race(:degree(16)) .map({hasShufflePair($_, $count)})); } In Raku, objects with the role Iterable have methods `hyper` and `race` that allow the iterable to be processed **in parallel** (`hyper` keeps the output order the same as the input order, and `race` the output order isn’t preserved; the documentation says the mnemonic is “in a race, you never know who will arrive first”). Doing this with parallel processing, it’s much faster: View the entire Raku script for this task on GitHub. $ time raku/ch-2.raku Example 1: Input: $from = 1, $to = 1000, $count = 1 Output: 0 Example 2: Input: $from = 1500, $to = 2500, $count = 1 Output: 3 Example 3: Input: $from = 1000000, $to = 1500000, $count = 5 Output: 2 Example 4: Input: $from = 13427000, $to = 14100000, $count = 2 Output: 11 Example 5: Input: $from = 1030, $to = 1130, $count = 1 Output: 2 real 0m16.659s user 2m8.096s sys 0m1.723s $ time raku/ch-2.raku Example 1: Input: $from = 1, $to = 1000, $count = 1 Output: 0 Example 2: Input: $from = 1500, $to = 2500, $count = 1 Output: 3 Example 3: Input: $from = 1000000, $to = 1500000, $count = 5 Output: 2 Example 4: Input: $from = 13427000, $to = 14100000, $count = 2 Output: 11 Example 5: Input: $from = 1030, $to = 1130, $count = 1 Output: 2 real 0m16.659s user 2m8.096s sys 0m1.723s ## Perl Interestingly, running the non-parallel version in Perl was **way** faster: 0m18.853s. With that kind of speed, I didn’t feel the need to try to make it use more than one core of my computer. Note: on line 7, I’m appending an empty string to explicitly cast the values as strings. use List::AllUtils qw( sum ); sub isSameLength($A, $B) { length($A."") == length($B.""); } sub isPair($A, $B) { join('', sort split //, $A) eq join('', sort split //, $B); } sub hasShufflePair($A, $min) { my $count = 0; my $k = 2; while (1) { my $B = $A * $k++; # stop processing $A if $B has more digits than $A return 0 unless isSameLength($A, $B); # go to the next $k if $A & $B aren't combinations # of the same digits next unless isPair($A, $B); # it's a shuffle pair, count it if (++$count >= $min) { # abort searching if we found the minimum count of pairs return 1; } } return 0; } sub shufflePairs($from, $to, $count) { sum(map { hasShufflePair($_, $count) } $from .. $to); } use List::AllUtils qw( sum ); sub isSameLength($A, $B) { length($A."") == length($B.""); } sub isPair($A, $B) { join('', sort split //, $A) eq join('', sort split //, $B); } sub hasShufflePair($A, $min) { my $count = 0; my $k = 2; while (1) { my $B = $A * $k++; # stop processing $A if $B has more digits than $A return 0 unless isSameLength($A, $B); # go to the next $k if $A & $B aren't combinations # of the same digits next unless isPair($A, $B); # it's a shuffle pair, count it if (++$count >= $min) { # abort searching if we found the minimum count of pairs return 1; } } return 0; } sub shufflePairs($from, $to, $count) { sum(map { hasShufflePair($_, $count) } $from .. $to); } View the entire Perl script for this task on GitHub. ## Python Python was also able to run the non-parallel version in 0m20.718s, so I didn’t bother trying to speed it up. def is_same_length(A, B): return len(str(A)) == len(str(B)) def is_pair(A, B): return( "".join(sorted(list(str(A))))=="".join(sorted(list(str(B)))) ) def has_shuffle_pair(A, m): count = 0 k = 2 while True: B = A * k k += 1 # stop processing $A if $B has more digits than $A if not is_same_length(A, B): return 0 # go to the next $k if $A & $B aren't combinations # of the same digits if is_pair(A, B): # it's a shuffle pair, count it count += 1 if (count >= m): # abort searching if we found the minimum count of pairs return 1 def shuffle_pairs(frm, to, count): return sum([ has_shuffle_pair(i, count) for i in range(frm, to+1) ]) def is_same_length(A, B): return len(str(A)) == len(str(B)) def is_pair(A, B): return( "".join(sorted(list(str(A))))=="".join(sorted(list(str(B)))) ) def has_shuffle_pair(A, m): count = 0 k = 2 while True: B = A * k k += 1 # stop processing $A if $B has more digits than $A if not is_same_length(A, B): return 0 # go to the next $k if $A & $B aren't combinations # of the same digits if is_pair(A, B): # it's a shuffle pair, count it count += 1 if (count >= m): # abort searching if we found the minimum count of pairs return 1 def shuffle_pairs(frm, to, count): return sum([ has_shuffle_pair(i, count) for i in range(frm, to+1) ]) View the entire Python script for this task on GitHub. ## Elixir For the non-parallel version, script execution took `0m42.355s`, so I set out to figure out how to execute `has_shuffle_pairs/2` asynchronously. But running it asynchronously didn’t get much of a time gain: `0m30.461s`. def is_same_length(a, b) do a |> Integer.to_string |> String.length == b |> Integer.to_string |> String.length end def is_pair(a, b) do a |> Integer.to_string |> String.codepoints |> Enum.sort == b |> Integer.to_string |> String.codepoints |> Enum.sort end def has_shuffle_pair(a, m, count, k) do b = a * k if not is_same_length(a, b) do 0 # stop processing a if b has more digits than a else if not is_pair(a, b) do # go to the next k if a & b aren't combinations # of the same digits has_shuffle_pair(a, m, count, k+1) else # it's a shuffle pair, count it count = count + 1 if count >= m do 1 # abort searching if we found the min count of pairs else has_shuffle_pair(a, m, count, k+1) end end end end def has_shuffle_pair(a, m) do has_shuffle_pair(a, m, 0, 2) end def shuffle_pairs(from, to, count) do stream = from .. to |> Task.async_stream( fn i -> has_shuffle_pair(i, count) end, ordered: false ) Enum.sum_by(stream, fn {:ok, num} -> num end) end def is_same_length(a, b) do a |> Integer.to_string |> String.length == b |> Integer.to_string |> String.length end def is_pair(a, b) do a |> Integer.to_string |> String.codepoints |> Enum.sort == b |> Integer.to_string |> String.codepoints |> Enum.sort end def has_shuffle_pair(a, m, count, k) do b = a * k if not is_same_length(a, b) do 0 # stop processing a if b has more digits than a else if not is_pair(a, b) do # go to the next k if a & b aren't combinations # of the same digits has_shuffle_pair(a, m, count, k+1) else # it's a shuffle pair, count it count = count + 1 if count >= m do 1 # abort searching if we found the min count of pairs else has_shuffle_pair(a, m, count, k+1) end end end end def has_shuffle_pair(a, m) do has_shuffle_pair(a, m, 0, 2) end def shuffle_pairs(from, to, count) do stream = from .. to |> Task.async_stream( fn i -> has_shuffle_pair(i, count) end, ordered: false ) Enum.sum_by(stream, fn {:ok, num} -> num end) 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-350/packy-anderson ### _Related_
packy.dardan.com
December 2, 2025 at 1:25 AM
Reposted by Packy Anderson
Perl Weekly Challenge: The Power of String

With Perl Weekly Challenge 349's tasks being "Power String" and "Meeting Point", I had a tough music choice. I could have given you Kansas from the 70s, but my high school/college years were the 80s, so I had to go with my heart and give you The Power […]
Original post on fosstodon.org
fosstodon.org
November 27, 2025 at 3:38 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Ticking away the moments that make up a challenge…

I mean, with Perl Weekly Challenge 348's tasks being "String Alike" and "Convert Time", how could I pick anything else besides Time? Though I was amused by Mohammad Sajid Anwar initially mistyping the second task as […]
Original post on fosstodon.org
fosstodon.org
November 20, 2025 at 5:05 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Oh, oh, formatted strings, give me some thing…

Perl Weekly Challenge 347's tasks are formatting dates and phone numbers, so I'm offering up ELO's Telephone Line as this week's musical theme. Doo-wop, doo-be-doo-doo-wop, doo-wah, doo-languages...

https://packy.dardan.com/b/dp
Perl Weekly Challenge: Oh, oh, formatted strings, give me some thing…
Perl Weekly Challenge 347‘s tasks are formatting dates and phone numbers, so I’m offering up ELO’s Telephone Line as this week’s musical theme. Doo-wop, doo-be-doo-doo-wop, doo-wah, doo-languages… * Task 1: Format Date * Approach * Raku * Perl * Python * Elixir * Task 2: Format Phone Number * Approach * Elixir * Raku * Perl * Python # Task 1: Format Date You are given a date in the form: `10th Nov 2025`. Write a script to format the given date in the form: `2025-11-10` using the set below. @DAYS = ("1st", "2nd", "3rd", ....., "30th", "31st") @MONTHS = ("Jan", "Feb", "Mar", ....., "Nov", "Dec") @YEARS = (1900..2100) **Example 1** Input: $str = "1st Jan 2025" Output: "2025-01-01" **Example 2** Input: $str = "22nd Feb 2025" Output: "2025-02-22" **Example 3** Input: $str = "15th Apr 2025" Output: "2025-04-15" **Example 4** Input: $str = "23rd Oct 2025" Output: "2025-10-23" **Example 5** Input: $str = "31st Dec 2025" Output: "2025-12-31" ## Approach Well, how difficult this will be to do depends on how much validation we want to do of the input. Do we want to accept days with the incorrect ordinal (`22th` instead of `22nd`)? Do we want to accept years outside the given year range (`2525`)? I’m going to take the approach that I’m going to do some validation on the input first, then format the date, even though that’s going to wind up being most of the solution. Validating the days **could** be done by building a list of `@DAYS` like shown above, or we could use a regular expression to grab the numeric portion and the ordinal, and then test to see if the number is 1, 21, or 31, the ordinal should be “st”, if the number is 2 or 22, the ordinal should be “nd”, if the number is 3 or 23, the ordinal should be “rd”. and otherwise the ordinal should be “th”. Similarly, the years we can just compare `1900 <= year <= 2100`. The months, however, make sense to enumerate in their entirety. ## Raku But, because I need to not only check to see if the month provided is in the set of `@MONTHS`, but then translate that to a number, rather than storing the set as an array, I’m storing it as a hash, which allows me to test for membership AND translate to a numeric value. One of the things I like about Raku is the ability to do a conditional like `1900 <= $year <= 2100`. my %months = ( Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun => 6, Jul => 7, Aug => 8, Sep => 9, Oct => 10, Nov => 11, Dec => 12 ); sub formatDate($str) { my ($day, $month, $year) = $str.split(" "); my $m = $day ~~ /(\d+)(\D+)/; return "$day has the incorrect ordinal" unless (($m[0] == 1 || $m[0] == 21 || $m[0] == 31) && $m[1] eq "st") || (($m[0] == 2 || $m[0] == 22) && $m[1] eq "nd") || (($m[0] == 3 || $m[0] == 23) && $m[1] eq "rd") || $m[1] eq "th"; $day = $m[0]; # grab just the numeric portion return "Unknown month '$month'" unless %months{$month}:exists; $month = %months{$month}; # convert to numeric return "Year must be between 1900-2100" unless 1900 <= $year <= 2100; my $date; try { $date = Date.new($year, $month, $day); } return $! if $!; return qq/"$date"/; } my %months = ( Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun => 6, Jul => 7, Aug => 8, Sep => 9, Oct => 10, Nov => 11, Dec => 12 ); sub formatDate($str) { my ($day, $month, $year) = $str.split(" "); my $m = $day ~~ /(\d+)(\D+)/; return "$day has the incorrect ordinal" unless (($m[0] == 1 || $m[0] == 21 || $m[0] == 31) && $m[1] eq "st") || (($m[0] == 2 || $m[0] == 22) && $m[1] eq "nd") || (($m[0] == 3 || $m[0] == 23) && $m[1] eq "rd") || $m[1] eq "th"; $day = $m[0]; # grab just the numeric portion return "Unknown month '$month'" unless %months{$month}:exists; $month = %months{$month}; # convert to numeric return "Year must be between 1900-2100" unless 1900 <= $year <= 2100; my $date; try { $date = Date.new($year, $month, $day); } return $! if $!; return qq/"$date"/; } View the entire Raku script for this task on GitHub. $ raku/ch-1.raku Example 1: Input: $str = "1st Jan 2025" Output: "2025-01-01" Example 2: Input: $str = "22nd Feb 2025" Output: "2025-02-22" Example 3: Input: $str = "15th Apr 2025" Output: "2025-04-15" Example 4: Input: $str = "23rd Oct 2025" Output: "2025-10-23" Example 5: Input: $str = "31st Dec 2025" Output: "2025-12-31" Example Year Too Big: Input: $str = "31st Dec 2525" Output: Year must be between 1900-2100 Example Year Too Small: Input: $str = "31st Dec 1825" Output: Year must be between 1900-2100 Example Bad Ordinal: Input: $str = "31nd Dec 2025" Output: 31nd has the incorrect ordinal Example Bad Month: Input: $str = "30th Avril 2025" Output: Unknown month 'Avril' Example Bad Date: Input: $str = "31st Feb 2025" Output: Day out of range. Is: 31, should be in 1..28 $ raku/ch-1.raku Example 1: Input: $str = "1st Jan 2025" Output: "2025-01-01" Example 2: Input: $str = "22nd Feb 2025" Output: "2025-02-22" Example 3: Input: $str = "15th Apr 2025" Output: "2025-04-15" Example 4: Input: $str = "23rd Oct 2025" Output: "2025-10-23" Example 5: Input: $str = "31st Dec 2025" Output: "2025-12-31" Example Year Too Big: Input: $str = "31st Dec 2525" Output: Year must be between 1900-2100 Example Year Too Small: Input: $str = "31st Dec 1825" Output: Year must be between 1900-2100 Example Bad Ordinal: Input: $str = "31nd Dec 2025" Output: 31nd has the incorrect ordinal Example Bad Month: Input: $str = "30th Avril 2025" Output: Unknown month 'Avril' Example Bad Date: Input: $str = "31st Feb 2025" Output: Day out of range. Is: 31, should be in 1..28 ## Perl There wasn’t much translation necessary going from Raku to Perl. I was able to directly capture the result of the regex match, instead of needing to put it in a match object. and my year conditional needed an and and repeating the variable: `1900 <= $year && $year <= 2100`. I used `Time::Local` to translate the year, month, day into a epoch timestamp, and then `Time::Piece` to format that as a date string. use Time::Local; use Time::Piece; my %months = ( Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun => 6, Jul => 7, Aug => 8, Sep => 9, Oct => 10, Nov => 11, Dec => 12 ); sub formatDate($str) { my ($day, $month, $year) = split / /, $str; my ($dnum, $dord) = $day =~ /(\d+)(\D+)/; return "$day has the incorrect ordinal" unless (($dnum == 1 || $dnum == 21 || $dnum == 31) && $dord eq "st") || (($dnum == 2 || $dnum == 22) && $dord eq "nd") || (($dnum == 3 || $dnum == 23) && $dord eq "rd") || $dord eq "th"; $day = $dnum; # grab just the numeric portion return "Unknown month '$month'" unless exists $months{$month}; $month = $months{$month}; # convert to numeric return "Year must be between 1900-2100" unless 1900 <= $year && $year <= 2100; my $date; eval { my $epoch = timelocal(0, 0, 0, $day, $month-1, $year); $date = Time::Piece->new($epoch); }; if (my $err = $@) { # get rid of line info in the error $err =~ s{at .+ line \d+.\n}{}; return $err; } return qq/"@{[ $date->date ]}"/; } use Time::Local; use Time::Piece; my %months = ( Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun => 6, Jul => 7, Aug => 8, Sep => 9, Oct => 10, Nov => 11, Dec => 12 ); sub formatDate($str) { my ($day, $month, $year) = split / /, $str; my ($dnum, $dord) = $day =~ /(\d+)(\D+)/; return "$day has the incorrect ordinal" unless (($dnum == 1 || $dnum == 21 || $dnum == 31) && $dord eq "st") || (($dnum == 2 || $dnum == 22) && $dord eq "nd") || (($dnum == 3 || $dnum == 23) && $dord eq "rd") || $dord eq "th"; $day = $dnum; # grab just the numeric portion return "Unknown month '$month'" unless exists $months{$month}; $month = $months{$month}; # convert to numeric return "Year must be between 1900-2100" unless 1900 <= $year && $year <= 2100; my $date; eval { my $epoch = timelocal(0, 0, 0, $day, $month-1, $year); $date = Time::Piece->new($epoch); }; if (my $err = $@) { # get rid of line info in the error $err =~ s{at .+ line \d+.\n}{}; return $err; } return qq/"@{[ $date->date ]}"/; } View the entire Perl script for this task on GitHub. ## Python Python has a match object like Raku, and I was able to catch the exception from a bad date without much fuss. from datetime import date import re months = { 'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12 } def format_date(dstr): day, month, year = dstr.split() m = re.match(r'(\d+)(\D+)', day) dnum = int(m.group(1)) # make sure it's an int dord = m.group(2) if not ( ((dnum == 1 or dnum == 21 or dnum == 31) and dord == "st") or ((dnum == 2 or dnum == 22) and dord == "nd") or ((dnum == 3 or dnum == 23) and dord == "rd") or dord == "th" ): return f"{day} has the incorrect ordinal" day = dnum # grab just the numeric portion if not month in months: return f"Unknown month '{month}'" month = months[month] # convert to numeric year = int(year) # make sure it's an int if not (1900 <= year <= 2100): return "Year must be between 1900-2100" try: dateobj = date(year, month, day) except ValueError as err: return err return f'"#{dateobj.strftime("%Y-%m-%d")}"' from datetime import date import re months = { 'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12 } def format_date(dstr): day, month, year = dstr.split() m = re.match(r'(\d+)(\D+)', day) dnum = int(m.group(1)) # make sure it's an int dord = m.group(2) if not ( ((dnum == 1 or dnum == 21 or dnum == 31) and dord == "st") or ((dnum == 2 or dnum == 22) and dord == "nd") or ((dnum == 3 or dnum == 23) and dord == "rd") or dord == "th" ): return f"{day} has the incorrect ordinal" day = dnum # grab just the numeric portion if not month in months: return f"Unknown month '{month}'" month = months[month] # convert to numeric year = int(year) # make sure it's an int if not (1900 <= year <= 2100): return "Year must be between 1900-2100" try: dateobj = date(year, month, day) except ValueError as err: return err return f'"#{dateobj.strftime("%Y-%m-%d")}"' View the entire Python script for this task on GitHub. ## Elixir For Elixir, I offloaded most of the validation to guards on the `format_date/4` function, but I couldn’t think of a way to do the multipart `if` validation for the ordinal via a guard. Fortunately, I was able to make it a condition to return from `format_date/1` with an error. I also like that `Date.new/4` doesn’t throw an exception by default. If you want an exception, you use `Date.new!/4`. def format_date(_, _, _, year) when year < 1900, do: "Year must be between 1900-2100" def format_date(_, _, _, year) when year > 2100, do: "Year must be between 1900-2100" def format_date(_, month, mnum, _) when is_nil(mnum), do: "Unknown month '#{month}'" def format_date(dnum, _, mnum, year) do with {:ok, date} <- Date.new(year, mnum, dnum) do "\"#{date |> Date.to_iso8601}\"" else {:error, :invalid_date} -> "Invalid date" end end def format_date(str) do [day, month, year] = String.split(str) m = Regex.named_captures(~r'(?<dnum>\d+)(?<dord>\D+)', day) dnum = m["dnum"] |> String.to_integer # make sure it's an int if not ( ((dnum == 1 or dnum == 21 or dnum == 31) and m["dord"] == "st") or ((dnum == 2 or dnum == 22) and m["dord"] == "nd") or ((dnum == 3 or dnum == 23) and m["dord"] == "rd") or m["dord"] == "th" ) do "#{day} has the incorrect ordinal" else year = year |> String.to_integer # make sure it's an int mnum = Map.get(%{ "Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4, "May" => 5, "Jun" => 6, "Jul" => 7, "Aug" => 8, "Sep" => 9, "Oct" => 10, "Nov" => 11, "Dec" => 12 }, month) format_date(dnum, month, mnum, year) end end def format_date(_, _, _, year) when year < 1900, do: "Year must be between 1900-2100" def format_date(_, _, _, year) when year > 2100, do: "Year must be between 1900-2100" def format_date(_, month, mnum, _) when is_nil(mnum), do: "Unknown month '#{month}'" def format_date(dnum, _, mnum, year) do with {:ok, date} <- Date.new(year, mnum, dnum) do "\"#{date |> Date.to_iso8601}\"" else {:error, :invalid_date} -> "Invalid date" end end def format_date(str) do [day, month, year] = String.split(str) m = Regex.named_captures(~r'(?<dnum>\d+)(?<dord>\D+)', day) dnum = m["dnum"] |> String.to_integer # make sure it's an int if not ( ((dnum == 1 or dnum == 21 or dnum == 31) and m["dord"] == "st") or ((dnum == 2 or dnum == 22) and m["dord"] == "nd") or ((dnum == 3 or dnum == 23) and m["dord"] == "rd") or m["dord"] == "th" ) do "#{day} has the incorrect ordinal" else year = year |> String.to_integer # make sure it's an int mnum = Map.get(%{ "Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4, "May" => 5, "Jun" => 6, "Jul" => 7, "Aug" => 8, "Sep" => 9, "Oct" => 10, "Nov" => 11, "Dec" => 12 }, month) format_date(dnum, month, mnum, year) end end View the entire Elixir script for this task on GitHub. * * * # Task 2: Format Phone Number You are given a phone number as a string containing digits, space and dash only. Write a script to format the given phone number using the below rules: 1. Removing all spaces and dashes 2. Grouping digits into blocks of length 3 from left to right 3. Handling the final digits (4 or fewer) specially: - 2 digits: one block of length 2 - 3 digits: one block of length 3 - 4 digits: two blocks of length 2 4. Joining all blocks with dashes **Example 1** Input: $phone = "1-23-45-6" Output: "123-456" **Example 2** Input: $phone = "1234" Output: "12-34" **Example 3** Input: $phone = "12 345-6789" Output: "123-456-789" **Example 4** Input: $phone = "123 4567" Output: "123-45-67" **Example 5** Input: $phone = "123 456-78" Output: "123-456-78" ## Approach Really, this is the simpler of the two. The rules lay it all out: we strip all non-digits from the string, then we pull off three characters from the front of the string until the length of the string is 4 or less characters, then we group the remaining characters as directed. ## Elixir Because I had an inspiration about how to do this recursively with multiple dispatch, I decided to do the Elixir solution first. One thing I knew was that I wanted to immediately break the string into a list of characters, because while you **can’t** test the length of a string in a function’s guard clause, you **can** test the length of a list. However, I wasn’t sure of how to pull elements off the left-hand side of the list. `List.first/2` would give me the first element, but it wouldn’t return the list with the first element removed. After a bit of searching, I found the functions I wanted… in the `Kernel` module! * `Kernel.hd/1` returns the first element, or “head”, of a list * `Kernel.tl/1` returns **all but** the first element of a list, or the “tail” of the list Plus, because they’re `Kernel` modules, I wouldn’t need to qualify them with `Kernel.`, I could just use `hd()` and `tl()`. I think this shows off the power of multiple dispatch pretty well: the `format_phone/1` function (lines 21-26) accepts the string, strips out all non-numeric digit characters, splits the string into a list of those characters, and then passes the list to `format_phone/2` with an empty `formatted` string. We then have three multiple dispatch definitions of `format_phone/2`: one on lines 14-19 that handles all of the rule 2 processing (pulling the first three characters off the list, and then appending them to the formatted string along with a trailing dash, and then recursively calling `format_phone/2`), one on lines 8-12 that handles the list when it’s down to 4 elements (pulling the first two characters off the list, appending them to the formatted string, adding a dash to the formatted string, then joining and appending the remaining two characters to the formatted string and returning it), and one on lines 4-7 that handles the list when it’s less than 4 elements long (joining the remaining characters and appending them to the formatted string, which already has a trailing dash, and then returning that result). def format_phone(list, formatted) when length(list) < 4 do formatted <> Enum.join(list) end def format_phone(list, formatted) when length(list) == 4 do {a, list} = {hd(list), tl(list)} {b, list} = {hd(list), tl(list)} formatted <> "#{a}#{b}-" <> Enum.join(list) end def format_phone(list, formatted) when length(list) > 4 do {a, list} = {hd(list), tl(list)} {b, list} = {hd(list), tl(list)} {c, list} = {hd(list), tl(list)} format_phone(list, formatted <> "#{a}#{b}#{c}-") end def format_phone(phone) do phone |> String.replace(~r/\D/, "") |> String.graphemes |> format_phone("") end def format_phone(list, formatted) when length(list) < 4 do formatted <> Enum.join(list) end def format_phone(list, formatted) when length(list) == 4 do {a, list} = {hd(list), tl(list)} {b, list} = {hd(list), tl(list)} formatted <> "#{a}#{b}-" <> Enum.join(list) end def format_phone(list, formatted) when length(list) > 4 do {a, list} = {hd(list), tl(list)} {b, list} = {hd(list), tl(list)} {c, list} = {hd(list), tl(list)} format_phone(list, formatted <> "#{a}#{b}#{c}-") end def format_phone(phone) do phone |> String.replace(~r/\D/, "") |> String.graphemes |> format_phone("") end View the entire Elixir script for this task on GitHub. $ elixir/ch-2.exs Example 1: Input: $phone = "1-23-45-6" Output: "123-456" Example 2: Input: $phone = "1234" Output: "12-34" Example 3: Input: $phone = "12 345-6789" Output: "123-456-789" Example 4: Input: $phone = "123 4567" Output: "123-45-67" Example 5: Input: $phone = "123 456-78" Output: "123-456-78" $ elixir/ch-2.exs Example 1: Input: $phone = "1-23-45-6" Output: "123-456" Example 2: Input: $phone = "1234" Output: "12-34" Example 3: Input: $phone = "12 345-6789" Output: "123-456-789" Example 4: Input: $phone = "123 4567" Output: "123-45-67" Example 5: Input: $phone = "123 456-78" Output: "123-456-78" ## Raku And, as always happens when I do the Elixir solution first, it informs how I do the Raku version. Because Raku allows us to put `where` clauses testing string length into our function signatures, I didn’t need to pass the string around as a list of characters. And the `Str` class’ `.substr` routine let me pull the characters I wanted out of the string easily. multi formatPhone($phone where $phone.chars < 4, $formatted) { $formatted ~ $phone; } multi formatPhone($phone where $phone.chars == 4, $formatted) { $formatted ~ $phone.substr(0..1) ~ "-" ~ $phone.substr(2..3); } multi formatPhone($phone, $formatted) { formatPhone( $phone.substr(3), $formatted ~ $phone.substr(0..2) ~ "-" ); } multi formatPhone(Str $phone is copy) { $phone ~~ s:global/\D+//; formatPhone($phone, ""); } multi formatPhone($phone where $phone.chars < 4, $formatted) { $formatted ~ $phone; } multi formatPhone($phone where $phone.chars == 4, $formatted) { $formatted ~ $phone.substr(0..1) ~ "-" ~ $phone.substr(2..3); } multi formatPhone($phone, $formatted) { formatPhone( $phone.substr(3), $formatted ~ $phone.substr(0..2) ~ "-" ); } multi formatPhone(Str $phone is copy) { $phone ~~ s:global/\D+//; formatPhone($phone, ""); } View the entire Raku script for this task on GitHub. ## Perl Even though Perl doesn’t have multiple dispatch, I didn’t miss it that much. I just gave the recursive function a different name, and put all the clauses in an `if-elsif-else` block within that function. sub formatPhone2($phone, $formatted) { if (length($phone) < 4) { $formatted . $phone; } elsif (length($phone) == 4) { $formatted . substr($phone, 0,2) . "-" . substr($phone, 2,2); } else { formatPhone2( substr($phone, 3), $formatted . substr($phone, 0, 3) . "-" ); } } sub formatPhone($phone) { $phone =~ s/\D+//g; formatPhone2($phone, ""); } sub formatPhone2($phone, $formatted) { if (length($phone) < 4) { $formatted . $phone; } elsif (length($phone) == 4) { $formatted . substr($phone, 0,2) . "-" . substr($phone, 2,2); } else { formatPhone2( substr($phone, 3), $formatted . substr($phone, 0, 3) . "-" ); } } sub formatPhone($phone) { $phone =~ s/\D+//g; formatPhone2($phone, ""); } View the entire Perl script for this task on GitHub. ## Python The big thing I need to remember in Python after writing Elixir, Raku, and Perl solutions first is that I have to **explicitly** return values from functions. import re def format_phone2(phone, formatted): if len(phone) < 4: return formatted + phone elif len(phone) == 4: return formatted + phone[0:2] + "-" + phone[2:4] else: return format_phone2( phone[3:], formatted + phone[0:3] + "-" ) def format_phone(phone): phone = re.sub(r'\D', '', phone) return format_phone2(phone, "") import re def format_phone2(phone, formatted): if len(phone) < 4: return formatted + phone elif len(phone) == 4: return formatted + phone[0:2] + "-" + phone[2:4] else: return format_phone2( phone[3:], formatted + phone[0:3] + "-" ) def format_phone(phone): phone = re.sub(r'\D', '', phone) return format_phone2(phone, "") View the entire Python script for this task on GitHub. * * * Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-347/packy-anderson ### _Related_
packy.dardan.com
November 12, 2025 at 3:02 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Whoa-oh-oh! Sing about parens!

With Perl Weekly Challenge 346's tasks being "Longest Parenthesis" and "Magic Expression", I had a bunch of ideas for music, so I went with the first one that popped into my head: Billy Joel's "The Longest Time". Who knows how much further […]
Original post on fosstodon.org
fosstodon.org
November 5, 2025 at 4:41 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Teach Them How To Say PWC…

With Perl Weekly Challenge 345's tasks being "Peak Positions" and "Last Visitor", I was somewhat at a loss for what to what music to pick. Then I thought about how I haven't done musical theater in a while, and it came to me: "One Last Time" […]
Original post on fosstodon.org
fosstodon.org
October 29, 2025 at 1:41 AM
Reposted by Packy Anderson
Perl Weekly Challenge: A-ray Sunshine!

The tasks for Perl Weekly Challenge 344 both have names that talk about being arrays, and nothing musically jumped out at me relating to arrays. So I started free-associating, and arrays made me think of rays, and rays of sunshine, and sunshine made me […]
Original post on fosstodon.org
fosstodon.org
October 21, 2025 at 1:10 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Ze-ro the CHAMPIONS!

Perl Weekly Challenge 343 starts with "Zero Friend" (which made me think of Carole King), but with task two being "Champion Team", I couldn't pick any music besides We Are The Champions by Queen. So now let's hope I don't make any bad mistakes while […]
Original post on fosstodon.org
fosstodon.org
October 14, 2025 at 3:07 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Balanced Adagio for Strings

I'm stretching the musical theme for Perl Weekly Challenge 342. Task 1 is "Balanced String", and when I was thinking about strings, I thought of Samuel Barber's Adagio for Strings. But don't cry—we'll have fun with "Balanced String" and "Max […]
Original post on fosstodon.org
fosstodon.org
October 8, 2025 at 4:16 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Something just BROKE…

The show my wife is currently performing in is Stephen Sondheim's Assassins (my favorite Sondheim musical), so with the first task being "Broken Keyboard", I needed to use Something Just Broke... But everybody's got the right to be happy, and this […]
Original post on fosstodon.org
fosstodon.org
October 1, 2025 at 12:21 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Rising in Num

Each week, I'm playing word association with the tasks to pick the musical theme. Originally, this started because one of the tasks was literally a line from a musical. This week, Perl Weekly Challenge 340 was more difficult with the tasks "Duplicate […]
Original post on fosstodon.org
fosstodon.org
September 23, 2025 at 4:02 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Points? What’s the diff?

With Perl Weekly Challenge 339 being "Max Diff" and "Peak Point", I started to think that "Point" was my hook into a musical theme. and that made me think of one of my favorite artists, David Wilcox. So please enjoy his song Turning Point while we […]
Original post on fosstodon.org
fosstodon.org
September 17, 2025 at 3:41 AM
Reposted by Packy Anderson
Supreme Court Rules 6-3 That Fundamental Interests Of United States Of America Would Be Irreparably Harmed If It Race-Based Harassment And Detention By Masked Thugs Were Even Temporarily Halted
September 8, 2025 at 5:16 PM
Reposted by Packy Anderson
Perl Weekly Challenge: Maxwell’s Silver Highest

Perl Weekly Challenge 338: "Highest Row" and "Max Distance"... Bang, Bang! Maxwell's Silver Highest came down upon her head! Bang, Bang! Maxwell's Silver Highest made sure she was dead! Let's have some fun!

https://packy.dardan.com/b/b0
Perl Weekly Challenge: Maxwell’s Silver Highest
Perl Weekly Challenge 338: “Highest Row” and “Max Distance”… Bang, Bang! Maxwell’s Silver Highest came down upon her head! Bang, Bang! Maxwell’s Silver Highest made sure she was dead! Let’s have some fun! * Task 1: Highest Row * Approach * Raku * Perl * Python * Elixir * Task 2: Max Distance * Approach * Raku * Perl * Python * Elixir # Task 1: Highest Row You are given a `m x n` matrix. Write a script to find the highest row sum in the given matrix. **Example 1** Input: @matrix = ([4, 4, 4, 4], [10, 0, 0, 0], [2, 2, 2, 9]) Output: 16 Row 1: 4 + 4 + 4 + 4 => 16 Row 2: 10 + 0 + 0 + 0 => 10 Row 3: 2 + 2 + 2 + 9 => 15 **Example 2** Input: @matrix = ([1, 5], [7, 3], [3, 5]) Output: 10 **Example 3** Input: @matrix = ([1, 2, 3], [3, 2, 1]) Output: 6 **Example 4** Input: @matrix = ([2, 8, 7], [7, 1, 3], [1, 9, 5]) Output: 17 **Example 5** Input: @matrix = ([10, 20, 30], [5, 5, 5], [0, 100, 0], [25, 25, 25]) Output: 100 ## Approach This is essentially a two-step matrix reduction: first we find the sums of each of the rows, then we get the maximum value for these sums. ## Raku In Raku, we can get this down to one line: sub highestRow(@matrix) { @matrix.map({ $_.sum }).max } sub highestRow(@matrix) { @matrix.map({ $_.sum }).max } View the entire Raku script for this task on GitHub. $ raku/ch-1.raku Example 1: Input: @matrix = ([4, 4, 4, 4], [10, 0, 0, 0], [2, 2, 2, 9]) Output: 16 Example 2: Input: @matrix = ([1, 5], [7, 3], [3, 5]) Output: 10 Example 3: Input: @matrix = ([1, 2, 3], [3, 2, 1]) Output: 6 Example 4: Input: @matrix = ([2, 8, 7], [7, 1, 3], [1, 9, 5]) Output: 17 Example 5: Input: @matrix = ([10, 20, 30], [5, 5, 5], [0, 100, 0], [25, 25, 25]) Output: 100 $ raku/ch-1.raku Example 1: Input: @matrix = ([4, 4, 4, 4], [10, 0, 0, 0], [2, 2, 2, 9]) Output: 16 Example 2: Input: @matrix = ([1, 5], [7, 3], [3, 5]) Output: 10 Example 3: Input: @matrix = ([1, 2, 3], [3, 2, 1]) Output: 6 Example 4: Input: @matrix = ([2, 8, 7], [7, 1, 3], [1, 9, 5]) Output: 17 Example 5: Input: @matrix = ([10, 20, 30], [5, 5, 5], [0, 100, 0], [25, 25, 25]) Output: 100 ## Perl In Perl, we need to import `max` and `sum` from a CPAN module, and I like List::AllUtils. use List::AllUtils qw( max sum ); sub highestRow($matrix) { max map { sum @$_ } @$matrix; } use List::AllUtils qw( max sum ); sub highestRow($matrix) { max map { sum @$_ } @$matrix; } View the entire Perl script for this task on GitHub. ## Python In Python, we need to use a list comprehension instead of `map`. def highest_row(matrix): return max( [ sum(row) for row in matrix ] ) def highest_row(matrix): return max( [ sum(row) for row in matrix ] ) View the entire Python script for this task on GitHub. ## Elixir In Elixir, we just use `Enum.map/2` to sum the rows, and pipe that through `Enum.max/3`. def highest_row(matrix) do Enum.map(matrix, fn row -> Enum.sum(row) end) |> Enum.max end def highest_row(matrix) do Enum.map(matrix, fn row -> Enum.sum(row) end) |> Enum.max end View the entire Elixir script for this task on GitHub. * * * # Task 2: Max Distance You are given two integer arrays, `@arr1` and `@arr2`. Write a script to find the maximum difference between any pair of values from both arrays. **Example 1** Input: @arr1 = (4, 5, 7) @arr2 = (9, 1, 3, 4) Output: 6 With element $arr1[0] = 4 | 4 - 9 | = 5 | 4 - 1 | = 3 | 4 - 3 | = 1 | 4 - 4 | = 0 max distance = 5 With element $arr1[1] = 5 | 5 - 9 | = 4 | 5 - 1 | = 4 | 5 - 3 | = 2 | 5 - 4 | = 1 max distance = 4 With element $arr1[2] = 7 | 7 - 9 | = 2 | 7 - 1 | = 6 | 7 - 3 | = 4 | 7 - 4 | = 4 max distance = 6 max (5, 6, 6) = 6 **Example 2** Input: @arr1 = (2, 3, 5, 4) @arr2 = (3, 2, 5, 5, 8, 7) Output: 6 With element $arr1[0] = 2 | 2 - 3 | = 1 | 2 - 2 | = 0 | 2 - 5 | = 3 | 2 - 5 | = 3 | 2 - 8 | = 6 | 2 - 7 | = 5 max distance = 6 With element $arr1[1] = 3 | 3 - 3 | = 0 | 3 - 2 | = 1 | 3 - 5 | = 2 | 3 - 5 | = 2 | 3 - 8 | = 5 | 3 - 7 | = 4 max distance = 5 With element $arr1[2] = 5 | 5 - 3 | = 2 | 5 - 2 | = 3 | 5 - 5 | = 0 | 5 - 5 | = 0 | 5 - 8 | = 3 | 5 - 7 | = 2 max distance = 3 With element $arr1[3] = 4 | 4 - 3 | = 1 | 4 - 2 | = 2 | 4 - 5 | = 1 | 4 - 5 | = 1 | 4 - 8 | = 4 | 4 - 7 | = 3 max distance = 4 max (5, 6, 3, 4) = 6 **Example 3** Input: @arr1 = (2, 1, 11, 3) @arr2 = (2, 5, 10, 2) Output: 9 With element $arr1[0] = 2 | 2 - 2 | = 0 | 2 - 5 | = 3 | 2 - 10 | = 8 | 2 - 2 | = 0 max distance = 8 With element $arr1[1] = 1 | 1 - 2 | = 1 | 1 - 5 | = 4 | 1 - 10 | = 9 | 1 - 2 | = 1 max distance = 9 With element $arr1[2] = 11 | 11 - 2 | = 9 | 11 - 5 | = 6 | 11 - 10 | = 1 | 11 - 2 | = 9 max distance = 9 With element $arr1[3] = 3 | 3 - 2 | = 1 | 3 - 5 | = 2 | 3 - 10 | = 7 | 3 - 2 | = 1 max distance = 7 max (8, 9, 9, 7) = 9 **Example 4** Input: @arr1 = (1, 2, 3) @arr2 = (3, 2, 1) Output: 2 With element $arr1[0] = 1 | 1 - 3 | = 2 | 1 - 2 | = 1 | 1 - 1 | = 0 max distance = 2 With element $arr1[1] = 2 | 2 - 3 | = 1 | 2 - 2 | = 0 | 2 - 1 | = 1 max distance = 1 With element $arr1[2] = 3 | 3 - 3 | = 0 | 3 - 2 | = 1 | 3 - 1 | = 2 max distance = 2 max (2, 1, 2) = 2 **Example 5** Input: @arr1 = (1, 0, 2, 3) @arr2 = (5, 0) Output: 5 With element $arr1[0] = 1 | 1 - 5 | = 4 | 1 - 0 | = 1 max distance = 4 With element $arr1[1] = 0 | 0 - 5 | = 5 | 0 - 0 | = 0 max distance = 5 With element $arr1[2] = 2 | 2 - 5 | = 3 | 2 - 0 | = 2 max distance = 3 With element $arr1[3] = 3 | 3 - 5 | = 2 | 3 - 0 | = 3 max distance = 3 max (4, 5, 3, 3) = 5 ## Approach I am not going to present the explanatory text from the examples this time, because it will bloat the solution. Since the pair of values with the max distance has to take one value from each list, the max distance will come from **only** one of two pairings: `|max(@arr1) - min($arr2)|` or `|max(@arr2) - min(@arr2)|`. ## Raku And this is all accomplished in Raku through the `min`, `max`, and `abs`. sub maxDistance(@arr1, @arr2) { my ($min1, $max1) = (@arr1.min, @arr1.max); my ($min2, $max2) = (@arr2.min, @arr2.max); max(abs($max1 - $min2), abs($max2 - $min1)); } sub maxDistance(@arr1, @arr2) { my ($min1, $max1) = (@arr1.min, @arr1.max); my ($min2, $max2) = (@arr2.min, @arr2.max); max(abs($max1 - $min2), abs($max2 - $min1)); } View the entire Raku script for this task on GitHub. $ raku/ch-2.raku Example 1: Input: @arr1 = (4, 5, 7) @arr2 = (9, 1, 3, 4) Output: 6 Example 2: Input: @arr1 = (2, 3, 5, 4) @arr2 = (3, 2, 5, 5, 8, 7) Output: 6 Example 3: Input: @arr1 = (2, 1, 11, 3) @arr2 = (2, 5, 10, 2) Output: 9 Example 4: Input: @arr1 = (1, 2, 3) @arr2 = (3, 2, 1) Output: 2 Example 5: Input: @arr1 = (1, 0, 2, 3) @arr2 = (5, 0) Output: 5 $ raku/ch-2.raku Example 1: Input: @arr1 = (4, 5, 7) @arr2 = (9, 1, 3, 4) Output: 6 Example 2: Input: @arr1 = (2, 3, 5, 4) @arr2 = (3, 2, 5, 5, 8, 7) Output: 6 Example 3: Input: @arr1 = (2, 1, 11, 3) @arr2 = (2, 5, 10, 2) Output: 9 Example 4: Input: @arr1 = (1, 2, 3) @arr2 = (3, 2, 1) Output: 2 Example 5: Input: @arr1 = (1, 0, 2, 3) @arr2 = (5, 0) Output: 5 ## Perl Perl does the same thing, but we need to import `min` and `max` from a CPAN module, and I like List::AllUtils. use List::AllUtils qw( min max ); sub maxDistance($arr1, $arr2) { my ($min1, $max1) = (min(@$arr1), max(@$arr1)); my ($min2, $max2) = (min(@$arr2), max(@$arr2)); max(abs($max1 - $min2), abs($max2 - $min1)); } use List::AllUtils qw( min max ); sub maxDistance($arr1, $arr2) { my ($min1, $max1) = (min(@$arr1), max(@$arr1)); my ($min2, $max2) = (min(@$arr2), max(@$arr2)); max(abs($max1 - $min2), abs($max2 - $min1)); } View the entire Perl script for this task on GitHub. ## Python Python doesn’t need to pull in any libraries. def max_distance(arr1, arr2): (min1, max1) = (min(arr1), max(arr1)) (min2, max2) = (min(arr2), max(arr2)) return max(abs(max1 - min2), abs(max2 - min1)) def max_distance(arr1, arr2): (min1, max1) = (min(arr1), max(arr1)) (min2, max2) = (min(arr2), max(arr2)) return max(abs(max1 - min2), abs(max2 - min1)) View the entire Python script for this task on GitHub. ## Elixir I need to pay attention! When I first whipped up the Elixir solution, line 7 looked like this: Enum.max(abs(max1 - min2), abs(max2 - min1)) Enum.max(abs(max1 - min2), abs(max2 - min1)) And I wound up getting the following error: $ elixir/ch-2.exs Example 1: Input: @arr1 = (4, 5, 7) @arr2 = (9, 1, 3, 4) ** (FunctionClauseError) no function clause matching in Enum.max_sort_fun/1 The following arguments were given to Enum.max_sort_fun/1: # 1 5 Attempted function clauses (showing 2 out of 2): defp max_sort_fun(sorter) when is_function(sorter, 2) defp max_sort_fun(module) when is_atom(module) (elixir 1.18.3) lib/enum.ex:1919: Enum.max_sort_fun/1 (elixir 1.18.3) lib/enum.ex:1916: Enum.max/3 elixir/ch-2.exs:13: PWC.solution/2 elixir/ch-2.exs:18: (file) $ elixir/ch-2.exs Example 1: Input: @arr1 = (4, 5, 7) @arr2 = (9, 1, 3, 4) ** (FunctionClauseError) no function clause matching in Enum.max_sort_fun/1 The following arguments were given to Enum.max_sort_fun/1: # 1 5 Attempted function clauses (showing 2 out of 2): defp max_sort_fun(sorter) when is_function(sorter, 2) defp max_sort_fun(module) when is_atom(module) (elixir 1.18.3) lib/enum.ex:1919: Enum.max_sort_fun/1 (elixir 1.18.3) lib/enum.ex:1916: Enum.max/3 elixir/ch-2.exs:13: PWC.solution/2 elixir/ch-2.exs:18: (file) So I immediately went to the Elixir REPL to figure out where I went wrong. $ iex Erlang/OTP 27 [erts-15.2.7] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [dtrace] Interactive Elixir (1.18.3) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> {arr1, arr2} = {[4, 5, 7], [9, 1, 3, 4]} {[4, 5, 7], [9, 1, 3, 4]} iex(2)> arr1 [4, 5, 7] iex(3)> Enum.min(arr1) 4 iex(4)> Enum.max(arr1) 7 iex(5)> Enum.min(arr2) 1 iex(6)> Enum.max(arr2) 9 iex(7)> {min1, max1} = {Enum.min(arr1), Enum.max(arr1)} {4, 7} iex(8)> {min2, max2} = {Enum.min(arr2), Enum.max(arr2)} {1, 9} $ iex Erlang/OTP 27 [erts-15.2.7] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [dtrace] Interactive Elixir (1.18.3) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> {arr1, arr2} = {[4, 5, 7], [9, 1, 3, 4]} {[4, 5, 7], [9, 1, 3, 4]} iex(2)> arr1 [4, 5, 7] iex(3)> Enum.min(arr1) 4 iex(4)> Enum.max(arr1) 7 iex(5)> Enum.min(arr2) 1 iex(6)> Enum.max(arr2) 9 iex(7)> {min1, max1} = {Enum.min(arr1), Enum.max(arr1)} {4, 7} iex(8)> {min2, max2} = {Enum.min(arr2), Enum.max(arr2)} {1, 9} So it wasn’t the `Enum.max/3` call on line 5 that was causing my problem. iex(9)> Enum.max(abs(max1 - min2), abs(max2 - min1)) ** (FunctionClauseError) no function clause matching in Enum.max_sort_fun/1 The following arguments were given to Enum.max_sort_fun/1: # 1 5 Attempted function clauses (showing 2 out of 2): defp max_sort_fun(sorter) when is_function(sorter, 2) defp max_sort_fun(module) when is_atom(module) (elixir 1.18.3) lib/enum.ex:1919: Enum.max_sort_fun/1 (elixir 1.18.3) lib/enum.ex:1916: Enum.max/3 iex:9: (file) iex(9)> Enum.max(abs(max1 - min2), abs(max2 - min1)) ** (FunctionClauseError) no function clause matching in Enum.max_sort_fun/1 The following arguments were given to Enum.max_sort_fun/1: # 1 5 Attempted function clauses (showing 2 out of 2): defp max_sort_fun(sorter) when is_function(sorter, 2) defp max_sort_fun(module) when is_atom(module) (elixir 1.18.3) lib/enum.ex:1919: Enum.max_sort_fun/1 (elixir 1.18.3) lib/enum.ex:1916: Enum.max/3 iex:9: (file) Ok, so it was line 7! Is the `Kernel.abs/1` a problem? iex(9)> abs(max1 - min2) 6 iex(9)> abs(max1 - min2) 6 No, that’s not it. Wait…what’s the definition of `Enum.max/3` again? > `max(enumerable, sorter \\ &>=/2, empty_fallback \\ fn -> raise Enum.EmptyError end)` D’oh! Once again, I’m bitten by the perl-ish habit of just passing multiple values to a function and depending on the language to coerce those value into a **list**. Once I passed `Enum.max/3` a list, everything worked: iex(10)> Enum.max([abs(max1 - min2), abs(max2 - min1)]) 6 iex(11)> ^C BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution iex(10)> Enum.max([abs(max1 - min2), abs(max2 - min1)]) 6 iex(11)> ^C BREAK: (a)bort (A)bort with dump (c)ontinue (p)roc info (i)nfo (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution def max_distance(arr1, arr2) do {min1, max1} = {Enum.min(arr1), Enum.max(arr1)} {min2, max2} = {Enum.min(arr2), Enum.max(arr2)} Enum.max([abs(max1 - min2), abs(max2 - min1)]) end def max_distance(arr1, arr2) do {min1, max1} = {Enum.min(arr1), Enum.max(arr1)} {min2, max2} = {Enum.min(arr2), Enum.max(arr2)} Enum.max([abs(max1 - min2), abs(max2 - min1)]) 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-338/packy-anderson ### _Related_
packy.dardan.com
September 9, 2025 at 12:54 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Small, but Oddly Current

With Perl Weekly Challenge 337 being Smaller Than Current and an Odd Matrix, I figured I'd dig into my collection and pull out a track called Odd Ones on the Kanno Yōko (菅野 よう子) Cowboy Bebop soundtrack mini-album, Vitaminless. So with the music […]
Original post on fosstodon.org
fosstodon.org
September 3, 2025 at 4:07 AM
Reposted by Packy Anderson
Perl Weekly Challenge: The Score for Group Therapy

The tasks this week are "Equal Group" and "Final Score", and I already used The Final Countdown back in PWC 325, so I couldn't use that. So I went digging though my music collection, and I found this track from Steve Hackett's Highly Strung […]
Original post on fosstodon.org
fosstodon.org
August 27, 2025 at 3:57 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Fanfare for the Common Character

This week I could have picked Aaron Copeland's original arrangement of his work, but I really wanted to show off Keith Emerson's masterful arrangement for ELP's 1977 album. Perl Weekly Challenge 335.

https://packy.dardan.com/b/_V
August 23, 2025 at 5:05 AM
Reposted by Packy Anderson
Perl Weekly Challenge: Zero is Not the End of the Line

This week's challenge is about straight lines and duplicate zeros, so let's jump to the end of the line and listen to some musicians who were decidedly not zeroes. So let's live the life we please with Perl Weekly Challenge 333 […]
Original post on fosstodon.org
fosstodon.org
August 9, 2025 at 12:14 AM
Reposted by Packy Anderson
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.

https://packy.dardan.com/b/ZQ
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 * Approach * Raku * Perl * Python * Elixir * Task 2: Odd Letters * Approach * Raku * Perl * Python * Elixir # 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("-"); } 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" $ 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; } 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' >>> '-'.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('-')]) 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 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'; } 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 $ 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'; } 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' 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 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 ### _Related_
packy.dardan.com
July 30, 2025 at 12:06 AM
Reposted by Packy Anderson
This Perl Weekly Challenge has tasks “Last Word” and “Buddy Strings”, and that got me thinking about the spate of famous people dying lately, so I decided to give the “last word” to someone known not for words but for… his flugelhorn.

So now let’s see why Perl Weekly Challenge 331 feels so […]
Original post on fosstodon.org
fosstodon.org
July 25, 2025 at 3:24 AM
Reposted by Packy Anderson
Perl Weekly Challenge: [TITLE OF THE POST]

This Perl Weekly Challenge is all about algorithmic replacement, and in thinking about the second task I realized that algorithmic replacement was also possible in music. So let's modulate, hold a high note, and see if we can hear the title of Perl […]
Original post on fosstodon.org
fosstodon.org
July 19, 2025 at 5:03 PM
Reposted by Packy Anderson
Perl Weekly Challenge: It MUST be nice!

This week's Perl Weekly Challenge has a task that wants strings to be "nice", but if musical theater has taught me anything, it's that nice is different than good (remember "good"?). But my wife pointed out that if the task wants the string to be nice […]
Original post on fosstodon.org
fosstodon.org
July 13, 2025 at 4:57 PM