Perl Weekly Challenge: Week 360

Challenge 1:

Text Justifier

You are given a string and a width.

Write a script to return the string that centers the text within that width using asterisks * as padding.

Example 1
Input: $str = "Hi", $width = 5
Output: "*Hi**"

Text length = 2, Width = 5
Need 3 padding characters total
Left padding: 1 star, Right padding: 2 stars
Example 2
Input: $str = "Code", $width = 10
Output: "***Code***"

Text length = 4, Width = 10
Need 6 padding characters total
Left padding: 3 stars, Right padding: 3 stars
Example 3
Input: $str = "Hello", $width = 9
Output: "**Hello**"

Text length = 5, Width = 9
Need 4 padding characters total
Left padding: 2 stars, Right padding: 2 stars
Example 4
Input: $str = "Perl", $width = 4
Output: "Perl"

No padding needed
Example 5
Input: $str = "A", $width = 7
Output: "***A***"

Text length = 1, Width = 7
Need 6 padding characters total
Left padding: 3 stars, Right padding: 3 stars
Example 6
Input: $str = "", $width = 5
Output: "*****"

Text length = 0, Width = 5
Entire output is padding

I'm going to show the Perl solution first.

We create an output string as long as $width and fill it with asterisks.

my $output = '*' x $width;

Then we calculate an offset which is the $width minus the length of $str divided by 2 and replace length of $str asterisks in $output with $str using substr().

substr $output, ($width - length $str) / 2, length $str,  $str;

Finally, we print $output.

say $output;

(Full code on Github.)

I thought the Raku solution would work the same way and it mostly does except...

my $output = '*' x $width;

.substr() doesn't replace characters in immutable strings. You have to use the .substr-rw() method to do that.

$output.substr-rw(($width - $str.chars) / 2, $str.chars) = $str;
say $output;

(Full code on Github.)

Challenge 2:

Word Sorter

You are give a sentence.

Write a script to order words in the given sentence alphabetically but keeps the words themselves unchanged.

Example 1
Input: $str = "The quick brown fox"
Output: "brown fox quick The"
Example 2
Input: $str = "Hello    World!   How   are you?"
Output: "are Hello How World! you?"
Example 3
Input: $str = "Hello"
Output: "Hello"
Example 4
Input: $str = "Hello, World! How are you?"
Output: "are Hello, How World! you?"
Example 5
Input: $str = "I have 2 apples and 3 bananas!"
Output: "2 3 and apples bananas! have I"

This is the kind of problem both Raku and Perl can solve in one line.

The Raku version is especially compact and easy to read. We get the input from the first command-line argument, @*ARGS[0]. It is split up into words with the aptly named .words(). The words are sorted into ascending alphabetical order with .sort(). One small hurdle is that the sort must be case-insensitive. (See example 1 where The is output after quick not before brown as we would expect in pure ASCII order.) We can get around this by converting each word to upper-case while sorting it. We then join the sorted words together with one space in between using .join(). Luckily, the spec did not require us to preserve white space. Finally, the result is output with .say().

@*ARGS[0].words.sort({$^a.uc cmp $^b.uc}).join(q{ }).say

(Full code on Github.)

The Perl version is actually a little shorter as we can pack code a little tighter but it slightly harder to read IMO.

say join q{ }, sort {uc $a cmp uc $b} split /\s+/, shift

(Full code on Github.)